【问题标题】:Attaching pointers when removing duplicates from a doubly linked list从双向链表中删除重复项时附加指针
【发布时间】:2016-05-07 22:02:47
【问题描述】:

我正在实现一个从未排序的双向链表中删除重复项的函数。我删除了其他功能以使代码更简短、更清晰。请假设 InsertFront 等其他功能正常工作。

当我删除重复项时,我无法从我的 Node 类中附加 prev 指针。我不确定我是否正确附加了 prev 指针。我目前遇到段错误。谢谢。

这是我的头文件。 dlinkedlist.h

#ifndef _DLINKEDLIST_H_
#define _DLINKEDLIST_H_

#include <cstdlib>
#include <stdexcept>
#include <string>
#include <iostream> 

using namespace std;

template <class T>
class Node
{
public:
    T data;
    //string data;
    Node<T>* prev;
    Node<T>* next;

    // default constructor
    Node(T value)
    {
        data = value;
        prev = NULL;
        next = NULL;
    }
};

// DLinkedList class definition
template <class T>
class DLinkedList
{
private:
    // DLinkedList private members
    int size; // number of items stored in list
    Node<T>* front; // references to the front
    Node<T>* back;  //  and back of the list


DLinkedList();
DLinkedList(const DLinkedList& ll);
~DLinkedList();

    void RemoveDuplicates();
}

这是我的实现 dlinkedlist.cpp

#ifdef _DLINKEDLIST_H_

#include <cstdlib>
#include <stdexcept>
#include "dlinkedlist.h"
#include <string>
#include <iostream>
using namespace std;


template <class T>
void DLinkedList<T>::RemoveDuplicates() { 
    Node<T> *current, *runner, *dup;
    current = front;

    cout << "Removing the duplicates..." << endl;

    while (current != NULL && current->next != NULL) {

        runner = current;

        while (runner->next != NULL) {
            if(current->data == runner->next->data) {
                dup = runner->next;
                runner->next = runner->next->next;
                runner->next->prev = runner->next->prev->prev; //trying to connect to previous node. Works without this line.
                delete dup;
            }
            else {
                runner = runner->next;
            }
        }
        current = current->next;
    }

}

这是我的测试文件。测试.cpp

#include <cstdlib>
#include <iostream>
#include <string>
#include "ccqueue.h"
#include "dlinkedlist.h"
#include "ticket.h"

using namespace std;

void LLTest();

int main()
{
  cout << "\nEntering DLinkedList test function..." << endl;
  LLTest();
  cout << "...DLinkedList test function complete!\n" << endl;
  return 0;
}

void LLTest()
{
  // default constructor, InsertFront, InsertBack, ElementAt
  DLinkedList<int> lla;
  lla.InsertFront(2);
  cout << "-------------------------------------------------\n";
  lla.InsertBack(5);
  lla.InsertBack(10);
  lla.InsertBack(2);
  cout << "-------------------------------------------------\n";
  lla.RemoveDuplicates();
}

感谢您的宝贵时间。

【问题讨论】:

  • 我不知道为什么这会导致问题,但可能值得花一点时间看看将 runner-&gt;next-&gt;prev = runner-&gt;next-&gt;prev-&gt;prev; 更改为 runner-&gt;next-&gt;prev = runner; 是否会有所作为
  • 感谢您的回复。不幸的是,这不起作用并导致了段错误。
  • 双向链表很棘手。最好定义一个通用函数,例如 removeNode(),对其进行调试,然后将其用作 removeDuplicates() 函数的一部分。

标签: c++ pointers linked-list


【解决方案1】:
dup = runner->next;
runner->next = runner->next->next;
runner->next->prev = runner->next->prev->prev;

1) 在我指出的代码块的第二行之后,当链表的最后一个元素是要删除的元素时,runner-&gt;next 变为 NULL。所以下一行runner-&gt;next-&gt;prev 会崩溃,因为它等同于'NULL-&gt;next'。 所以你必须在第二行和第三行之间添加if (runner-&gt;next != NULL )。那就是你说“只有在下一个存在时才将下一个连接到上一个(仅当不是最后一个项目时)

2) 学习调试工具。

【讨论】:

  • 感谢您简明扼要地向我解释这一点。我将学习如何使用调试工具。
  • 是的,我认为它解决了问题。我只是通过前后打印来测试我的链表,但发现了一个新问题。当打印最后带有重复的链表时,它将删除所有重复项。反向打印时,末尾有重复,它将删除除最后一个之外的所有重复。如果我在背面使用唯一元素反向打印,它将反向打印,删除所有重复项。
  • 我不清楚你说什么,但是尝试为我们刚刚添加的 if 添加 else 部分。 else back = runner; /* set back to runner, since the back is getting deleted*/
  • 哇!我猜你解释了我试图正确描述的内容并改变 back = runner 解决了我的问题。非常感谢你的帮助!你不仅解决了我的一个问题,而且解决了两个问题。我真的很感谢你帮助我杰。
  • 不客气。需要注意的是,@Sam Varshavchik 给出的答案是完美且具有教学意义的。
【解决方案2】:

让我们看一下相关的代码部分,非常非常缓慢:

 dup = runner->next;

这就是你稍后会看到的delete。这是重复节点,ok。

 runner->next = runner->next->next;

如果您将上面的行更改为“runner->next = dup->next;”,这有助于提高可读性。这清楚地表明您正在“围绕它”重新链接dup 的前任,以将其指向dup 之后的下一个节点。

 runner->next->prev = runner->next->prev->prev; //trying to connect to previous node. Works without this line.

好的,runner-&gt;next 现在是最初在dup 之后的节点。现在,请稍等片刻,喝杯咖啡,然后问自己以下问题:

在双向链表中,假设节点ptr 不是列表中的最后一个节点,应该:

ptr->next->prev

永远是????

嗯,ptr 是列表中的一个节点。 ptr->next 是下一个节点。作为下一个节点的prev 值,您希望找到什么?

如果你向前迈出一步,然后又后退一步,你会在哪里结束?好吧,在这个宇宙中,你会在你开始的地方结束(忽略相对论的影响)。因此,在经典的双向链表中,ptr-&gt;next-&gt;prev 将是ptr,假设ptr 不是链表中的最后一个节点(注意这部分,它会变得很重要)。

所以,我会胡乱猜测,你的代码应该做的是:

 runner->next->prev = runner; // Relink to the previous node

但这也不是正确的答案。请记住,我们假设runner 不是双向链表中的最后一个节点。如果是,那么runner-&gt;next 显然是NULL,因为根据定义,runner 是列表中的最后一个节点(您删除了列表中的最后一个节点,所以runner 是新的最后一个节点列表),所以这里会发生空指针取消引用。

那么,如果runner 确实是列表中的最后一个节点,您认为这里应该发生什么?如果你说“什么都没有”,那么你的答案是正确的:

if (runner->next)
     runner->next->prev = runner;

【讨论】:

    【解决方案3】:
    dup = runner->next;
    runner->next = dup->next;
    if ( dup->next != NULL )
         dup->next->prev = runner;
    else 
         back = runner; /* since we are deleting last element */
    delete dup;
    

    这可能是一个更清晰的实现,它取代了:

     dup = runner->next;
     runner->next = runner->next->next;
     runner->next->prev = runner->next->prev->prev; //trying to connect to previous node. Works without this line.
     delete dup;
    

    【讨论】:

      猜你喜欢
      • 2011-08-18
      • 1970-01-01
      • 1970-01-01
      • 2021-05-04
      • 1970-01-01
      • 2019-04-02
      • 2018-09-16
      • 2013-11-15
      • 2012-10-06
      相关资源
      最近更新 更多