(剑指Offer)面试题13:在O(1)时间内删除链表结点

时间:2022-01-11 11:30:20

题目:

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

struct ListNode{
int val;
ListNode* next;
}; void deleteNode(ListNode** pListHead,ListNode* pToBeDeleted)

思路:

删除链表结点的一般思路是:从头指针开始遍历,找到要删除结点的前一结点pPrev,然后pPrev->next=pToBeDeleted->next,(还需考虑删除结点是否为头结点),这样的时间复杂度为O(n)。

而题目要求为O(1),找到删除结点的前一结点复杂度为O(n),但找到后一结点的复杂度为O(1),如果我们把后一结点的东西拷贝到要删除结点上,再把后一结点删除,岂不就是相当于把当前需要删除的结点删除了?

ListNode* pNext=pToBeDeleted->next;
pToBeDeleted->val=pNext->val;
pToBeDeleted->next=pNext->next;
delete pNext;
pNext=NULL;

考虑特殊情况,如果要删除的结点为头结点和尾结点怎么办?

头结点:要删除的结点为头结点,删除之后,链表为空,此时需要将头结点置为NULL,这时候改变的头指针的内存地址,因此需要传递二级指针,*pHead=NULL;

尾结点:要删除的结点为尾结点,尾结点没有后一结点,因此,不能通过上述方法来解决,还要回到传统的遍历方法,找到它的前一结点,然后将它的下一结点置为NULL,同时删除尾指针。

代码:

struct ListNode{
int val;
ListNode* next;
}; void deleteNode(ListNode** pListHead,ListNode* pToBeDeleted){
if(pListHead==NULL || pToBeDeleted==NULL)
return;
if(pToBeDeleted->next!=NULL){
ListNode* pNext=pToBeDeleted->next;
pToBeDeleted->val=pNext->val;
pToBeDeleted->next=pNext->next;
delete pNext;
pNext=NULL;
}
else if(*pListHead==pToBeDeleted){
delete pToBeDeleted;
pToBeDeleted=NULL;
*pListHead=NULL;
}
else{
ListNode* pNode=*pListHead;
while(pNode->next!=pToBeDeleted)
pNode=pNode->next;
pNode->next=NULL;
delete pToBeDeleted;
pToBeDeleted=NULL;
}
}