简单的链表删除失败

时间:2018-02-20 18:17:35

标签: c++ linked-list

我正在学习C ++,我尝试实现简单的单链表但删除节点部分失败。我无法理解为什么这个基本的delete_node部分失败了。看来prev-> delete_node方法中的set_next行无法正常工作。我也尝试调试它但未能发现错误。

using namespace std; //ignore it for simplicity

class Node {
    int data;
    Node *next;
public:
    Node() {}
    void set_data(int a_data)
    {
        data = a_data;
    }
    void set_next(Node *a_next)
    {
        next = a_next;
    }

    int get_data()
    {
        return data;
    }

    Node* get_next()
    {
        return next;
    }
};


class List {
    Node *head;
public:
    List()
    {
        head = NULL;
    }

    void print_list();
    void append_node(int data);
    void delete_node(int data);
};

void List::print_list()
{
    Node *temp = head;
    if(temp == NULL)
    {
        cout << "empty" << endl;
        return;
    }

    if(temp->get_next() == NULL)
    {
        cout << temp->get_data() << "--->";
        cout << "NULL" << endl;
    }
    else
    {
        do
        {
            cout << temp->get_data() << "+++>";
            temp = temp->get_next();
        } while(temp != NULL);
        cout << "NULL" << endl;
    }
}

void List::append_node(int data)
{
    Node *new_node = new Node();
    new_node->set_data(data);
    new_node->set_next(NULL);

    Node *temp = head;
    if(temp != NULL)
    {
        while(temp->get_next()!=NULL)
        {
            temp = temp->get_next();
        }
        temp->set_next(new_node);
    }
    else
    {
        head = new_node;
    }
}


void List::delete_node(int data)
{
    Node *temp = head;
    if(temp == NULL)
    {
        return;
    }
    else
    {
        Node *prev = NULL;
        do
        {
            prev = temp;
            if(temp->get_data() == data)
            {
                prev->set_next(temp->get_next());
                delete temp;
                break;
            }
            temp = temp->get_next();
        } while(temp!=NULL);
    }
}


int main()
{
    List list;
    list.append_node(10);
    list.append_node(20);
    list.append_node(30);
    list.append_node(40);
    list.append_node(50);
    list.append_node(60);

    list.delete_node(30); //

    list.print_list();
    return 0;
}

valgrind给了我以下错误。

==22232== Invalid read of size 8
==22232==    at 0x400D38: Node::get_next() (20_1.cpp:25)
==22232==    by 0x400A5E: List::print_list() (20_1.cpp:62)
==22232==    by 0x400C6C: main (20_1.cpp:127)
==22232==  Address 0x5abdd28 is 8 bytes inside a block of size 16 free'd
==22232==    at 0x4C2F24B: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==22232==    by 0x400BA8: List::delete_node(int) (20

4 个答案:

答案 0 :(得分:3)

让我们仔细看看List::delete_node函数

中的这些行
prev = temp;
if(temp->get_data() == data)
{
    prev->set_next(temp->get_next());
    delete temp;
    break;
}

第一个使prev指向temp所指向的同一节点。在此prev == temp为真之后。

所以当你这样做时

prev->set_next(temp->get_next());

相同
temp->set_next(temp->get_next());

也就是说,您将temp->next指向temp->next并且根本不会更改它。您永远不会取消链接节点与列表的链接,但您确实将其删除。这使得列表的打印无效,因为您将取消引用已删除的节点。

作为一个简单的解决方案,你可以这样做:

if (head->get_data() == data)
{
    // Special case: Head node is the one we want to delete
    Node* old_head = head;

    // Make the head be the second node in the list, if any
    head = head->get_next();

    // Delete the old head
    delete old_head;
}
else
{
    // We know it's not the head node of the list, use the "next" to find it
    for (Node* node = head; node->get_next() != 0; node = node->get_next())
    {
        if (node->get_next()->get_data() == data)
        {
            // It's the "next" node we want to remove
            Node* old_next = node->get_next();

            // Unlink the node
            node->set_next(node->get_next()->get_next());

            delete old_next;

            break;
        }
    }
}

答案 1 :(得分:2)

问题在于,do / while循环指针tempprev的开头指向相同的Node。因此,您重新指向节点,然后立即将其删除。

更好的方法是根本不使用prev。获取next,查看其数据是否与正在删除的数据匹配。如果是,请“绕过”并删除next。否则,请转到下一个节点,直到您点击NULL

void List::delete_node(int data) {
    if(head == NULL) {
        return;
    }
    if (head->get_data() == data) {
        Node *toDelete = head;
        head = head->get_next();
        delete toDelete;
        return;
    }
    Node *temp = head;
    for ( ; ; ) {
        Node *next = temp->get_next();
        if (next == null) {
            break;
        }
        if (next->get_data() == data) {
            temp->set_next(next->get_next());
            delete next;
            break;
        }
        temp = temp->get_next();
    }
}

答案 2 :(得分:0)

确切的工作解决方案是

void List::delete_node(int data)
{
    Node *temp = head;
    Node *prev = NULL;
    //first check whether its a parent element or not 
    if(temp && temp->get_data() == data){
      head = head->get_next();
      delete temp;
    }
    else{
      while (temp){
        if (temp->get_data() == data){
            if (prev)
                prev->set_next(temp->get_next());
            delete temp;
            return;
        }
        prev = temp;
        temp = temp->get_next();
      }
  }
}

这甚至适用于删除头节点

答案 3 :(得分:-1)

我发现您的代码存在许多问题。

您的Node构造函数未初始化任何Node成员。

您的List类缺少析构函数来释放任何已分配的节点。

您的print_list()append_node()实施比他们需要的更加冗长。

但是,最重要的是,对于您的特定问题,您的列表delete_node()方法无法正确管理其prev变量。 prev始终指向正在查看的当前节点,而不是指向已查看的上一个节点。因此,在删除节点时,您实际上并未正确更新链接。如果要删除的节点是head节点,您也不会更新列表的head成员。

尝试更像这样的东西:

class Node;

class List {
    Node *head;
public:
    List();
    ~List();

    void print_list();
    void append_node(int data);
    void delete_node(int data);
};

class Node {
    int data;
    Node *next;
public:
    Node(int a_data = 0, Node *a_next = NULL);

    void set_data(int a_data);
    void set_next(Node *a_next);

    int get_data();
    Node* get_next();

    friend class List;
};

Node::Node(int a_data, Node *a_next)
    : data(a_data), next(a_next)
{
}

void Node::set_data(int a_data)
{
    data = a_data;
}

void Node::set_next(Node *a_next)
{
    next = a_next;
}

int Node::get_data()
{
    return data;
}

Node* Node::get_next()
{
    return next;
}

List::List()
    : head(NULL)
{
}

List::~List()
{
    Node *temp = head;
    while (temp)
    {
        Node *next = temp->get_next();
        delete temp;
        temp = next;
    }
}

void List::print_list()
{
    Node *temp = head;
    if (!temp)
    {
        cout << "empty" << endl;
        return;
    }

    do
    {
        cout << temp->get_data();
        temp = temp->get_next();
        if (!temp) break;
        cout << "+++>";
    }
    while (true);

    cout << "--->NULL" << endl;
}

void List::append_node(int data)
{
    Node **temp = &head;
    while (*temp) temp = &((*temp)->next);
    *temp = new Node(data);
}

void List::delete_node(int data)
{
    Node *temp = head;
    Node *prev = NULL;

    while (temp)
    {
        if (temp->get_data() == data)
        {
            if (prev)
                prev->set_next(temp->get_next());
            if (temp == head)
                head = temp->get_next();
            delete temp;
            return;
        }
        prev = temp;
        temp = temp->get_next();
    }
}

int main()
{
    List list;
    list.append_node(10);
    list.append_node(20);
    list.append_node(30);
    list.append_node(40);
    list.append_node(50);
    list.append_node(60);

    list.delete_node(30); //

    list.print_list();
    return 0;
}