C++ 困惑于如何做双链接列表的深度复制?
我一直在尝试实现一个双链接列表的深度副本,但我一直遇到问题。我不得不做了几次不同的操作,但最终我得到了一个地址错误。我只需要解释一下如何正确地做 清单HC++ 困惑于如何做双链接列表的深度复制?,c++,visual-studio,doubly-linked-list,deep-copy,C++,Visual Studio,Doubly Linked List,Deep Copy,我一直在尝试实现一个双链接列表的深度副本,但我一直遇到问题。我不得不做了几次不同的操作,但最终我得到了一个地址错误。我只需要解释一下如何正确地做 清单H 类列表 { 公众: List(); ~List(); 列表(const List&c); 列表和运算符=(常量列表和t); 私人: 列表*下一步; 列表*Prev; 节点*头; List.cpp List::~List() { 节点*移动=头部; while(移动!=NULL) { 节点*temp=move->Next; 删除移动; 移动=温
类列表
{
公众:
List();
~List();
列表(const List&c);
列表和运算符=(常量列表和t);
私人:
列表*下一步;
列表*Prev;
节点*头;
List.cpp
List::~List()
{
节点*移动=头部;
while(移动!=NULL)
{
节点*temp=move->Next;
删除移动;
移动=温度;
}
}
列表::列表(常量列表和c)
{
name=c.name;
如果(c.Head==NULL){
Head=NULL;
}
否则{
Head=新节点(*c.Head);
节点*电流=头部;
节点*ObjHead=c.头部;
节点*CurrentObj=ObjHead;
while(当前->下一步!=NULL){
当前->下一步=新节点(当前对象->下一步->调味品);
}
}
}
您可以首先使用一个虚拟头。完成深度复制后,您可以将头设置为虚拟头的下一个,并删除虚拟头。这样,您也不必检查是否(c.head==NULL)
Node *ptr1, *ptr2;
head = ptr1 = new Node();
ptr2 = c.head;
while (ptr2)
{
ptr1->next = new Node(*ptr2);
ptr1 = ptr1->next;
ptr2 = ptr2->next;
}
Node *temp = head;
head = head->next;
delete temp;
您可以首先使用虚拟头。完成深度复制后,您可以将head
设置到虚拟头的下一个,并删除虚拟头。这样,您也不必检查是否(c.head==NULL)
Node *ptr1, *ptr2;
head = ptr1 = new Node();
ptr2 = c.head;
while (ptr2)
{
ptr1->next = new Node(*ptr2);
ptr1 = ptr1->next;
ptr2 = ptr2->next;
}
Node *temp = head;
head = head->next;
delete temp;
复制链接列表涉及三件事:
遍历正在复制的列表
从原始节点复制新节点
对于(2)中的每个新节点,尾部将其链接到链接列表
第一种是琐碎的,第二种是相当基本的,但第三种是经常让人产生循环的方法。对于复制ctor,一种方法是使用指向指针的指针。这允许我们通过自己的地址来寻址链接列表中的每个指针
List::List(const List& c)
: Head(nullptr)
, name(c.name)
{
Node *prev = nullptr;
Node **pp = &Head;
for (const Node *p = c.Head; p; p = p->Next)
{
// make a new node, storing its address at the
// pointer obtained by dereference of `pp`. the
// first iteration that will be the Head member.
*pp = new Node(*p);
(*pp)->Prev = prev;
prev = *pp;
// now just advance `pp` to point to the `Next`
// member of the node we just hung on the list.
pp = &(*pp)->Next;
}
*pp = nullptr; // terminate the list.
}
这假设您是节点
类支持复制构造(最好是这样)。但仅此而已。从中,您可以使用来制造复制分配运算符,并具有三个符合性列表类的基本规则。复制链接列表涉及三件事:
遍历正在复制的列表
从原始节点复制新节点
对于(2)中的每个新节点,尾部将其链接到链接列表
第一种是琐碎的,第二种是相当基本的,但第三种是经常让人产生循环的方法。对于复制ctor,一种方法是使用指向指针的指针。这允许我们通过自己的地址来寻址链接列表中的每个指针
List::List(const List& c)
: Head(nullptr)
, name(c.name)
{
Node *prev = nullptr;
Node **pp = &Head;
for (const Node *p = c.Head; p; p = p->Next)
{
// make a new node, storing its address at the
// pointer obtained by dereference of `pp`. the
// first iteration that will be the Head member.
*pp = new Node(*p);
(*pp)->Prev = prev;
prev = *pp;
// now just advance `pp` to point to the `Next`
// member of the node we just hung on the list.
pp = &(*pp)->Next;
}
*pp = nullptr; // terminate the list.
}
这假设您是节点
类支持复制构造(最好是)。但这就是所需要的。从那以后,您可以使用来制造副本分配运算符,并有一个三合规列表类的基本规则。首先对我来说似乎是不正确的设计。为什么您有list*prev,*next
?它们应该是Node*prev,*next
。实际上,它是一个列表的链接列表,不是节点。我有两个双链接列表。节点管理一个数据的双链接列表。然后列表管理节点。首先,对我来说似乎是不正确的设计。为什么有list*prev,*next
?它们应该是Node*prev,*next
。实际上,它是一个列表的链接列表,而不是节点。我有两个双链接列表d list.Node管理一个双链接的数据列表。然后list管理节点。@lucieon当我运行它并尝试从节点获取数据时,我会抛出一个异常。读取访问冲突。这是nullptr。不确定为什么会发生这种情况。我看不到这种情况会尝试读取nullptr
。尝试使函数const
像这样List&operator=(const List&t)const;
。还要确保在节点
类的默认构造函数中初始化指向nullptr
的指针。@luceion是的,我确保所有指针都设置为nullptr。我非常确定问题出在副本分配的某个地方。请尝试通过调试器运行它。@luceion当我运行它并尝试从节点获取数据时e我遇到一个异常。读取访问冲突。这是nullptr。不确定为什么会发生这种情况。我看不出有这种情况会尝试读取nullptr
。请尝试使函数const
像这样List&operator=(const List&t)const;
。还要确保在节点
类的默认构造函数中初始化指向nullptr
的指针。@luceion是的,我确保所有指针都设置为nullptr。我非常确定问题出在复制分配的某个地方。尝试通过调试器运行它。我的复制构造函数现在工作正常,但是,复制assignment是我遇到一些问题的地方。我试图在没有复制/交换方法的情况下完成它。使用Insert()不是更好吗
并从复制构造函数调用它?@TanveerBadar否。我只得到了关于此列表的信息,没有尾部指针。Insert
因此必须迭代到每次插入结束,因此将复制ctor操作转换为O(N^2),而不是上面看到的非常琐碎的O(N)。如果你想“更好”,它会“更好”为了不做任何疯狂的事情,只需首先使用std::list
。@WhozCraig是的,我确信使用std::list很容易,但我需要在没有它或任何其他容器的情况下实现它。所以据我所知,我还需要在复制赋值操作符中遍历列表。@Jay,你需要实现一个,是的。你需要在我的回答中,我的复制构造函数现在工作得很好,但是,复制赋值是我遇到一些问题的地方。我试图在没有复制/交换方法的情况下完成它。这不是很困难吗