【问题标题】:How to delete all items in a doubly linked list that match a certain value?如何删除双向链表中匹配某个值的所有项目?
【发布时间】:2016-05-31 08:01:36
【问题描述】:

我有一个任务,我必须编写一个函数,该函数可以删除双向链表的任何部分,其中存在具有预定输入的值。我在这方面工作了很长一段时间,我写的代码没有发现任何其他问题。作业是这样写的:

编写一个 C++ 函数来删除链表中的节点。该函数有两个参数——链表的头部和要删除的值。它应该删除所有包含要删除的值的节点。它应该返回链表的头部。

链表结构:

struct node
{
    int value;
    node *next;
    node *prev;
};
node *DeleteNode(node *head, int value){
node *tmp=head;
while(tmp!=NULL){
    if(tmp->value==value){
        if(tmp==head){
            head=head->next;
            head->prev=NULL;
            delete tmp;
            tmp=head;
        }
        else if(tmp->next==NULL){
            node *temp=tmp;
            temp->prev->next=NULL;
            delete tmp;
            tmp=temp;
        }
        else{
            node *node1=tmp;
            node1->prev->next=node1->next;
            node1->next->prev=node1->prev;
            delete tmp;
            tmp=node1;
        }
    }
    tmp=tmp->next;
}
return head;}

所以,当它运行测试时

1 2 3 4

需要删除所有的3s,得到的结果是

1 2 4

这是正确的。它适用于其他需要删除头部项目的示例,以及需要删除尾部的示例。它适用于所有需要的功能....除此之外...

2 2 2 2 65 83

需要删除2s的地方。它只是得到一个结果

2 65 83

因此,由于某种原因,额外的 2 被留下了。有什么猜测吗?我已经提供了我为这个问题提供的一切。我认为问题可能出在我删除双向链表中的中间项目的部分,但我完全迷失了。非常感谢!

【问题讨论】:

  • 我不明白else if(tmp->next==NULL){ 中发生的事情temp 指向与tmp 相同。你delete tmp,所以tmptemp 都指向垃圾,然后你将垃圾指针temp 分配回tmp。这将在tmp=tmp->next; 蓬勃发展
  • 再想一想,我认为你可以像地狱一样作弊,因为你在名单的最后。 tmp->prev->next=NULL; delete tmp; return head;
  • 所有三个答案都带来了重要且不同的细节。建议将它们全部考虑在内。
  • @tschadius 不要着急。即使您将实施答案中显示的解决方案,您的功能也将包含错误。:) 通常只有我的解决方案是正确的。:)
  • 你真的应该使用std::list,这是一个标准化的双链表实现。然后您可以使用std::remove()std::list::erase()(即所谓的Erase–remove idiom)来删除所有具有相同值的元素(或者您希望使用std::remove_if() 代替的任何条件)。

标签: c++ linked-list nodes doubly-linked-list


【解决方案1】:

这看起来很可疑(添加了一些空格):

node* node1 = tmp;
node1->prev->next = node1->next;
node1->next->prev = node1->prev;
delete tmp;
tmp = node1;

node1 只是tmp 的别名。它实际上没有任何其他用途。然后你 delete tmp 并将其分配给 node1 - 这正是你刚刚删除的内容!

你可能想这样做:

tmp->prev->next = tmp->next;
tmp->next->prev = tmp->prev;
node* prev = tmp->prev;
delete tmp;
tmp = prev; 

【讨论】:

    【解决方案2】:

    当您的初始头节点与值匹配时会出现问题。

    在此您将下一个节点设置为新头并摆脱旧节点,然后将 tmp 设置为新头,

    if(tmp==head){
            head=head->next;
            head->prev=NULL;
            delete tmp;
            tmp=head;
     }
    

    然后当你退出你设置的 if 语句时

    tmp = tmp-> next
    

    因此,在您的 if 块中,您将当前节点设置为下一个节点,然后删除曾经是当前节点的节点,然后当您退出 if 语句时,您再次向下移动到下一个节点,有效地跳过了新的头完全节点。

    【讨论】:

    • 是的,这解释了它。感谢您的帮助,我将 tmp=tmp->next 放入 else 块中,问题已解决。非常感谢:)
    【解决方案3】:

    我补充巴里的回答:

    node *temp=tmp;
    temp->prev->next=NULL;
    delete tmp;
    tmp=temp;
    

    这将为您的当前节点tmp设置一个别名temp,然后将您前一个节点的next指针设置为NULL,删除您的当前节点并将您的tmp指针设置为temp,但仅此而已只是您刚刚删除的节点的别名。您可能希望node *temp=tmp->prev; 将您的 tmp 设置为新的最后一个元素。

    可能导致您的错误的是,无论您与价值进行比较,您都会调用

    tmp=tmp->next;
    

    最后,转到您的下一个节点。您应该将其包装在 else 块中,以便仅在您的值不匹配时执行。

    【讨论】:

    • 就是这样!我将最后的 tmp=tmp->next 位包装到一个 else 块中,它就起作用了。感谢您的帮助!
    【解决方案4】:

    当定义一个双链表时,通常会定义一个包含两个指针的单独结构:一个指向链表头的指针,另一个指向链表尾的指针。例如

    struct list
    {
        node *head = nullptr;
        node *tail = nullptr;
        //...
    };
    

    在结构体内部也声明了处理列表的成员函数。

    尽管如此,如果使用您的方法,那么删除所有值等于给定值的节点的函数看起来就像在这个演示程序中显示的那样

    #include <iostream>
    
    struct node
    {
        int value;
        node *next;
        node *prev;
    };
    
    void display( node *head )
    {
        for ( ; head; head = head->next )
        {
            std::cout << head->value << ' ';
        }
        std::cout << std::endl;
    }
    
    node * AppendNode( node *head, int value )
    {
        node *tmp = new node { value, nullptr, nullptr };
    
        if ( !head )
        {
            head = tmp;
        }
        else
        {
            node *prev = head;
            while ( prev->next ) prev = prev->next;
            tmp->prev = prev;
            prev->next = tmp;
        }
    
        return head;
    }
    
    node * DeleteNode( node *head, int value )
    {
        for ( node *current = head; current; )
        {
            if ( current->value == value )
            {
                node *tmp = current;
    
                if ( current->next )
                {
                    current->next->prev = current->prev;
                }
    
                if ( current->prev )
                {
                    current->prev->next = current->next;
                    current = current->next;
                }
                else
                {
                    head = current->next;
                    current = head;
                }
    
                delete tmp;
            }
            else
            {
                current = current->next;
            }            
        }
    
        return head;
    }    
    
    int main()
    {
        node *head = nullptr;
    
        for ( int x : { 2, 2, 2, 2, 65, 83 } ) head = AppendNode( head, x );
    
        display( head );
    
        head = DeleteNode( head, 2 );
    
        display( head );
    
        head = DeleteNode( head, 83 );
    
        display( head );
    
        head = DeleteNode( head, 65 );
    
        display( head );
    }
    

    它的输出是

    2 2 2 2 65 83 
    65 83 
    65 
    

    【讨论】:

    • 这个答案通过旁道提出了一个问题,即链表实现是否应该负责甚至能够在没有帮助的情况下“删除 if”行为。我认为没有。
    • @user4581301 我可能没有理解您的评论。所以我的回答可能无关紧要。:) 不过 std::list 有方法 remove_if。
    • 你懂的。看来我和标准委员会意见不一。
    猜你喜欢
    • 2023-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多