我正在学习数据结构课程,目前的任务是创建一个简单的队列类,它是从现有的双向链表类构建的。
听起来很容易,但我很生疏,尤其是使用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_ */
我尝试用“家庭作业”来标记这一点,但显然这不再是一件事了。
虽然这是家庭作业,但我的任务是重复使用这个已编写的代码来创建一个新类。
在此先感谢,我非常感谢任何建议和解释。
迈克尔
答案 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