题目:
给你一个单链表的表头,再给你其中某个结点的指针,要你删除这个结点,条件是你的程序必须在O(1)的时间内完成删除。
基本思想:
普通的删除方法,首先通过表头,找到待删除结点(设为B)的前一个结点(设为A),将A的指向改一下就行,然后删除掉B结点就行了。要删除的结点一定要delete掉。
void DeleteNode_On(ListNode *pHead, ListNode *pDelete)
{
printf("delete:%d\n", pDelete->val);
ListNode *s = pHead;
while(s->next != pDelete)
s = s->next;
s->next = s->next->next;
delete(pDelete);
}
这个算法主要耗时在于查找前一个结点,所以是O(n)的算法。
那么既然要求是O(1),显然不能再去for一遍了,联想到数组的删除,这个问题就比较好解决了。
首先我们很容易就能得到待删除结点,即B结点的后一个结点C,然后将C的值赋值给B结点的值,相当于数组删除时候的覆盖,现在B结点和C结点一模一样了,接下来就相当简单了吧,我们不删B,直接利用B删掉C就行了,方法简单,时间O(1)。
如果要删除的节点位于链表的尾部,那么它就没有下一个节点。我们仍然需要从链表的头节点开始,顺序遍历。
如果链表中只有一个节点,此时我们在删除节点之后,还需要把链表的头结点设置为NULL。
void DeleteNode_O1(ListNode *pHead, ListNode *pDelete)
{
printf("delete:%d\n", pDelete->val);
if(pDelete->next != NULL) //如果pDelete不是尾结点, 则让后一个结点覆盖掉pDelete, 然后删除后一个结点
{
ListNode *pNext = pDelete->next;
pDelete->val = pNext->val;
pDelete->next = pNext->next;
delete(pNext);
pNext=NULL;
}
else if(*pHead == pDelete)//链表只有一个节点,删除头结点(也是尾节点)
{
delete(pDelete);
pDelete=NULL;
*pHead=NULL;
}
else// 如果链表中有多个节点,删除尾节点的情况,顺序遍历找到前节点
{
ListNode *s = *pHead;
while(s->next != pDelete)
s = s->next;
s->next=NULL;
delete(pDelete);
pDelete=NULL;
}
}
对于n-1个非尾节点,O(1)。
对于尾节点,O(n)。
平均时间复杂度:
[(n-1)*O(1)+O(n)]/n=O(1)。
引用: