剑指offer之删除链表结点

1.题目描述

在O(1)时间删除链表结点。给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 结点。
节点结构:

struct ListNode
{
    int       m_nValue;
    ListNode* m_pNext;
};

2.问题分析

因为是单链表,正常情况下,删除一个a节点,需要知道a节点的前驱节点,但是这样时间复杂度就是O(n)了,不符合要求。那么只有把a节点后面一个节点的值复制过来,在删除掉a节点的后面一个节点。

3.源码

#include <iostream>
using namespace std;
class ListNode
{
    friend ostream& operator <<(ostream&os,const ListNode& myListNode);
public:

    void clear()
    {
        if(this ==nullptr)
            return;

        ListNode* head = this;
        ListNode* pNext = nullptr;
        while(head!=nullptr)
        {
            pNext = head->pNext;
            delete head;
            head = pNext;
        }
    }

    bool removeNode(ListNode*& node)
    {
        if(!node||!this)
            return false;
        if(node->pNext !=nullptr )
        {
            ListNode* pNext = node->pNext;
            node->_value = pNext->_value;
            node->pNext = pNext->pNext;
            delete pNext;
            pNext = nullptr;
            return true;
        }
        else if(this == node)
        {
            delete node;
            node = nullptr;
            //this = nullptr;
            return true;
        }
        //删除的node在尾部或者不在
        else
        {

            ListNode* pNode = this;
            while(pNode->pNext != node && pNode !=nullptr)
            {
                pNode = pNode->pNext;
            }
            //删除节点是否在尾部
            if(pNode!=nullptr)
            {
                pNode->pNext =nullptr;
                delete node;
                node = nullptr;
                return true;
            }
            return false;
        }

    }

    ListNode():_value(-1),pNext(nullptr){}
    ListNode(int value):_value(value),pNext(nullptr){}
    int _value;
    ListNode * pNext;

};
ostream& operator <<(ostream&os,ListNode& head)
{
    ListNode* node = &head;
    if(node == nullptr)
        return os;
    while(node->pNext!=nullptr)
    {
        os<<node->_value<<" ";
        node = node->pNext;
    }
    os<<node->_value;
    return os;
}


int main()
{
    ListNode* number1 = new ListNode(1);
    ListNode* number2 = new ListNode(2);
    ListNode* number3 = new ListNode(3);
    ListNode* number4 = new ListNode(4);
    number3->pNext = number4;


    cout <<"delete number1 in number1 before:"<<*number1<<endl;
    number1->removeNode(number1);
    cout <<"delete number1 in number1 after:"<<*number1<<endl;

    number1->removeNode(number2);
    cout <<"delete number2 in number1 after:"<<*number1<<endl;

    number3->removeNode(number4);
    cout <<"delete number3 in number1 after:"<<*number3<<endl;


    return 0;
}

运行结果图:
这里写图片描述

注意: 上面虽然可以删除头结点,应为这是使用ListNode* & ,把 node = nullptr; 才把此指针置空。但是要删除链表中非头节点,虽然可以释放指针指向区域,但是指针自身值没有改变,就变成野指针。
比如:删除number3节点,其实是删除了number4节点,但是number4节点变成了野指针。修改main函数

    ListNode* number1 = new ListNode(1);
    ListNode* number2 = new ListNode(2);
    ListNode* number3 = new ListNode(3);
    ListNode* number4 = new ListNode(4);
    number3->pNext = number4;



    number3->removeNode(number3);
    cout <<"delete number3 in number3 after:"<<*number3<<endl;

    cout <<*number4 <<endl;

运行结果图:
这里写图片描述

这就是调用野指针出现的情况。

4.总结

此方法删除节点有一个问题就是,之前指向该节点的指针变成了野指针,无法在函数中令指针为空。删除一个节点之后,最好使该节点为nullptr。
本题有4点要注意:

  • 删除节点为空
  • 删除节点不在链表中
  • 该链表只有一个节点,删除节点既是头结点也是尾节点,删除头结点之后,指向头结点的指针为空
  • 该链表有多个节点,删除节点为尾节点,则需要找到尾节点的前驱节点

猜你喜欢

转载自blog.csdn.net/zqw_yaomin/article/details/81582776