在双向链表C ++中使用add方法的问题

时间:2014-02-24 02:12:32

标签: c++ doubly-linked-list

我正在学习数据结构课程,目前的任务是创建一个简单的队列类,它是从现有的双向链表类构建的。

听起来很容易,但我很生疏,尤其是使用c ++,我很难从书中获取双向链表代码。代码是有意义的(除了add方法),但是当我尝试调用addFront()时程序崩溃。

我有一种感觉我犯了一个愚蠢的错误,但如果我无法让示例代码正确运行,我显然需要一些帮助和解释。

教授建议我们使用的代码是Michael T. Goodrich在C ++的数据结构和算法的第127页。您实际上可以使用亚马逊的内部功能查看此页面。 http://amzn.com/0470383275

我正在尝试编译的文件可以在这里找到: https://dl.dropboxusercontent.com/u/12660663/DLinkedList.zip

这是作者的add front方法,它调用add()方法,我相信问题所在。

void DLinkedList::addFront(const Elem& e)   // add to front of list
{ add(header->next, e); }

这是添加函数的确切内容如何写在书中和教授的MS Word文档中充满了示例代码(顺便说一下完全在Comic Sans中):

// Insert new node before v
void DLinkedList::add(DNode* v, const Elem& e) 
{
DNode* u = new DNode;  u->elem = e;    // create a new node for e
u->next = v;                           // link u in between v
u->prev = v->prev;                     // ...and v->prev
v->prev->next = v->prev = u;

}

除了最后一行之外,这段代码是有道理的,我觉得很难理解。

这就是我正在做的所有事情,以使程序崩溃(请记住,该项目实际上是使用此类来创建另一个类,所以我只是想让它工作):

#include "DLinkedList.h"

int main()
{
    DLinkedList list;

    Elem s;
    s = "Jim";

    list.addFront(s);    // This and addBack(s) causes the program to crash,
                         //   doesn't crash if I remove this line  
    return 0;
}

这是头文件:

#include <string>
#include <iostream>
using namespace std;

#ifndef DLINKEDLIST_H_
#define DLINKEDLIST_H_

// Code Fragment 3.22
typedef string Elem;                // list element type
class DNode {                   // doubly linked list node
private:
  Elem elem;                    // node element value
  DNode* prev;              // previous node in list
  DNode* next;              // next node in list
  friend class DLinkedList;         // allow DLinkedList access
};

// Code Fragment 3.32
class DLinkedList {             // doubly linked list
public:
  DLinkedList();                // constructor
  ~DLinkedList();               // destructor
  bool empty() const;               // is list empty?
  const Elem& front() const;            // get front element
  const Elem& back() const;         // get back element
  void addFront(const Elem& e);     // add to front of list
  void addBack(const Elem& e);      // add to back of list
  void removeFront();               // remove from front
  void removeBack();                // remove from back
private:                    // local type definitions
  DNode* header;                // list sentinels
  DNode* trailer;
protected:                  // local utilities
  void add(DNode* v, const Elem& e);        // insert new node before v
  void remove(DNode* v);            // remove node v
};

#endif /* DLINKEDLIST_H_ */

我尝试用“家庭作业”来标记这一点,但显然这不再是一件事了。

虽然这是家庭作业,但我的任务是重复使用这个已编写的代码来创建一个新类。

在此先感谢,我非常感谢任何建议和解释。

迈克尔

3 个答案:

答案 0 :(得分:2)

GCC 4.7.3会为您发现令人困惑的代码生成警告。您可以按如下方式简化它(并删除警告):

void DLinkedList::add(DNode* v, const Elem& e) {
  DNode* u = new DNode;  u->elem = e;     // create a new node for e
  u->next = v;                            // link u in between v
  u->prev = v->prev;                      // ...and v->prev
  v->prev->next = u;
  v->prev = u;
}

最后,您的代码是segfaulting,因为您没有忠实地复制Goodrich的析构函数。它应该是:

DLinkedList::~DLinkedList()
{
  while (!empty()) removeFront();
  delete header;
  delete trailer;
}

答案 1 :(得分:1)

暂时回答你的一个问题:

void DLinkedList::add(DNode* v, const Elem& e) 
{
    DNode* u = new DNode;  u->elem = e;    // create a new node for e
    u->next = v;                           // link u in between v
    u->prev = v->prev;                     // ...and v->prev
    v->prev->next = v->prev = u;
}
  

除了最后一行之外,这段代码是有道理的,我觉得很难理解。

想象一下*v是链接列表中的一个节点:

... <--[-prev NODE next-]--> <--[-prev *v next-]--> <--[-prev NODE next-]--> ...

上面的代码是准备一个新节点*u

[-prev *u next-]          // DNode* u = new DNode;
[-prev *u elem=e next-]   // u->elem = e;

            [-prev *u elem=e next=v-]-   // u->next = v;
                                     \
                                      v
... <--[-prev NODE next-]--> <--[-prev *v next-]--> <--[-prev NODE next-]--> ...

         [-prev *u elem=e next=v-]-->   // u->prev = v->prev;
             \                       \
              v                       v
... <--[-prev NODE next-]--> <--[-prev *v next-]--> <--[-prev NODE next-]--> ...


         [-prev *u elem=e next=v-]-->   // v->prev->next = v->prev = u;
             \          ^   ^        \
              v          \   \        v
... <--[-prev NODE next-]-\   \-[-prev *v next-]--> <--[-prev NODE next-]--> ...

因此,最后一个v->prev->next = v->prev = u;打破了节点*v与列表中的节点之间的链接,使得:{/ p>

  • v->prev指向*u,以便在后向遍历期间*u之前找到*v
  • v->prev->next(指向下一个节点的前一次到*v的链接)指向*u

因此,通过列表的新排序是:node-that-used-be-immediately-previous-to - *v - &gt; *u - &gt; *v ....

答案 2 :(得分:0)

令我感到困惑的是add()的最后一行:

void DLinkedList::add(DNode* v, const Elem& e) {
    // ...
    v->prev->next = v->prev = u;
}

根据分配从右到左的规则,应简化为:

void DLinkedList::add(DNode* v, const Elem& e) {
    // ...
    v->prev = u;
    v->prev->next = u;
}

而不是Adam Burry所说的:

void DLinkedList::add(DNode* v, const Elem& e) {
  //...
  v->prev->next = u;
  v->prev = u;
}

但在我看来,我出错了。在谷歌待了一段时间之后,我在C ++中发现链式作业:

a = b = c = d;
// simplify to:
// a = d
// b = d
// c = d

而不是

// c = d
// b = c
// a = b