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点要注意:
- 删除节点为空
- 删除节点不在链表中
- 该链表只有一个节点,删除节点既是头结点也是尾节点,删除头结点之后,指向头结点的指针为空
- 该链表有多个节点,删除节点为尾节点,则需要找到尾节点的前驱节点