【问题标题】:How to properly return inserted element in linked ilist如何正确返回链表中的插入元素
【发布时间】:2021-09-05 14:35:23
【问题描述】:

我正在为双向链表实现插入(在元素之前)和添加(在元素之后)函数。我的问题是我必须返回的价值的差异以及它们为什么不同。

对于我的插入函数Link* insert(Link* n),我必须return n。对于我的添加函数Link* add(Link* n),我有return this。我已经测试了当我不这样做时会发生什么,之​​前/之后的元素不会出现。我想知道为什么会这样,为什么我不能return n(或return this)同时使用insert/add

#include "std_lib_facilities.h"

class Link {
public:
   string value; 

   Link(const string& v, Link* p = nullptr, Link* s = nullptr)
      : value{v}, prev{p}, succ{s} { }

   Link* insert(Link* n) ;   // insert n before this object
   Link* add(Link* n) ;      // insert n after this object
   Link* erase() ;           // remove this object from list
   Link* find(const string& s);    // find s in list
   const Link* find(const string& s) const; // find s in list

   Link* advance(int n) const;     // move n positions in list

   Link* next() const { return succ; }
   Link* previous() const { return prev; }

private:
   Link* prev;
   Link* succ;
};

//------------------------------------------------------------------------------

Link* Link::insert(Link* n)   // insert n before this object; return n
{
   if (n == nullptr) return this;
   n->succ = this;          
   if (prev) prev->succ = n; 
   n->prev = prev;        
   prev = n;                 
   return n;   // why?           
}

//------------------------------------------------------------------------------

Link* Link::add(Link* n)   // insert n after this object
{
  if (n == nullptr) return this;
  n->prev = this;
  if (succ) succ->prev = n;
  n->succ = succ;      
  succ = n;      
  return this; // why?
}

//------------------------------------------------------------------------------

Link* Link::erase()
{
   if (succ) succ->prev = prev; // if (succ != nullptr)
   if (prev) prev->succ = succ;
   return succ;
}

//------------------------------------------------------------------------------

Link* Link::find(const string& s) // find s in list;
// return 0 for "not found"
{
   Link* p = this;
   while(p) {
      if (p->value == s) return p;
      p = p->succ;
   }
   return 0;
}

//------------------------------------------------------------------------------

void print_all(Link* p)
{
   cout << "{ ";
   while (p) {
      cout << p->value;
      if ( (p = p->next()) ) cout <<  ", ";
   }
   cout << " }";
}

//------------------------------------------------------------------------------

int main()
{
   Link* norse_gods = new Link{"Thor"};
   norse_gods = norse_gods->insert(new Link{"Odin"});
   norse_gods = norse_gods->insert(new Link{"Zeus"});
   norse_gods = norse_gods->insert(new Link{"Freia"});

   Link* greek_gods = new Link{"Hera"};
   greek_gods = greek_gods->add(new Link{"Athena"}); 
   greek_gods = greek_gods->add(new Link{"Mars"});
   greek_gods = greek_gods->add(new Link{"Poseidon"});

   Link* p = greek_gods->find("Mars");
   if (p) p->value = "Ares";

   // Move Zeus into his correct Pantheon: 
   {
      Link* p = norse_gods->find("Zeus");
      if (p) {
         if (p==norse_gods) norse_gods = p->next();
         p->erase();
         greek_gods = greek_gods->insert(p);
      }
   }

   // Finally, let's print out those lists:

   print_all(norse_gods);
   cout<<"\n";

   print_all(greek_gods);
   cout<<"\n";
}

//------------------------------------------------------------------------------

【问题讨论】:

  • 您的Link 类同时充当列表本身和列表中的每个节点。您应该分离职责,即Link 应该只是value 的数据存储,然后创建一个管理Link 实例列表的List 类。您的所有列表操作(添加、插入、擦除、查找等)都应移至这个新的List 类。然后add()insert() 可以返回新创建的Link 节点。
  • @kino 向编写代码的人提出这个问题。:)

标签: c++ class linked-list dynamic-memory-allocation doubly-linked-list


【解决方案1】:

我认为函数insert和add的返回值不同的原因是当函数insert应用于代表头节点的节点时,头节点改变了,新插入的节点成为头节点。

另一方面,当在头节点之后添加新节点时,头节点保持不变。

所以这两个函数都返回指向头节点的指针。

如果在电源中考虑这个代码sn-p

Link* norse_gods = new Link{"Thor"};
norse_gods = norse_gods->insert(new Link{"Odin"});
norse_gods = norse_gods->insert(new Link{"Zeus"});
norse_gods = norse_gods->insert(new Link{"Freia"});

然后在成员函数的这些调用之后插入头节点是包含字符串"Freia"的节点。也就是说这些调用总是在头节点之前插入一个新节点,新插入的模式成为头节点。

在这段代码中,主要是 sn-p

Link* greek_gods = new Link{"Hera"};
greek_gods = greek_gods->add(new Link{"Athena"}); 
greek_gods = greek_gods->add(new Link{"Mars"});
greek_gods = greek_gods->add(new Link{"Poseidon"});

在函数的这些调用之后添加头节点是包含字符串"Hera"的节点。即在头节点之后插入新节点。指向头节点的指针保持不变。

在输出列表的函数中的两种情况

void print_all(Link* p)
{
   cout << "{ ";
   while (p) {
      cout << p->value;
      if ( (p = p->next()) ) cout <<  ", ";
            ^^^^^^^^^^^^^
   }
   cout << " }";
}

你可以使用数据成员succ在一个正向遍历它

Link* next() const { return succ; }
                     ^^^^^^^^^^^^

【讨论】:

  • 是否可以修改这两个函数,以便它们都可以返回相同的内容?此代码取自 Stroustrup 的 PPP。是因为正如@Remy Lebeau 所说,我应该允许这种情况发生的责任分离吗?
  • @kino 我描述了函数背后的逻辑。不清楚你为什么要打破逻辑。
猜你喜欢
  • 2020-07-25
  • 2019-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多