33.在O(1)时间删除链表结点

http://zhedahht.blog.163.com/blog/static/254111742007112255248202/

题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:

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

函数的声明如下:

void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);

分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,更重要的是,还能考察我们对时间复杂度的理解。

常规思路:

在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺序查找,时间复杂度自然就是O(n) 了。

我们之所以需要从头结点开始查找要删除的结点,是因为我们需要得到要删除的结点的前面一个结点。我们试着换一种思路。我们可以从给定的结点得到它的下一个结点。这个时候我们实际删除的是它的下一个结点,由于我们已经得到实际删除的结点的前面一个结点,因此完全是可以实现的。当然,在删除之前,我们需要需要把给定的结点的下一个结点的数据拷贝到给定的结点中。此时,时间复杂度为O(1)。

上面的思路还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,怎么办?我们仍然从链表的头结点开始,顺便遍历得到给定结点的前序结点,并完成删除操作。这个时候时间复杂度是O(n)。

那题目要求我们需要在O(1)时间完成删除操作,我们的算法是不是不符合要求?实际上,假设链表总共有n个结点,我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,仍然为O(1)。

删除节点是最后一个节点:1* O(n)

删除节点不是最后一个节点:(n-1)*O(1)

总的时间复杂度T(n) =[(n-1)*O(1)+O(n)]/n=
O(1)


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

void DeleteNode(ListNode* head,ListNode *toBeDeleted)
{// T(n)= [1*O(n)+(n-1)*O(1)]/n = O(1)
if (NULL==head || NULL==toBeDeleted)
return;

if (toBeDeleted->m_pNext==NULL)
{// toBeDeleted is the last node in list
//regular delete, 1*O(n)

ListNode *cur = head;
while(cur->m_pNext!=toBeDeleted)
{
cur = cur->m_pNext;
}
cur->m_pNext = NULL;

// delete deleted node
delete toBeDeleted;
toBeDeleted = NULL;
}
else
{// toBeDeleted is NOT the last node in list
// (n-1)*O(1)

ListNode *next = toBeDeleted->m_pNext;
toBeDeleted->m_nKey = next->m_nKey;
toBeDeleted->m_pNext = next->m_pNext;

// delete the node next to deleted
delete next;
next = NULL;
}
}

值得注意的是,为了让代码看起来简洁一些,上面的代码基于两个假设:(1)给定的结点的确在链表中;(2)给定的要删除的结点不是链表的头结点。不考虑第一个假设对代码的鲁棒性是有影响的。至于第二个假设,当整个列表只有一个结点时,代码会有问题。但这个假设不算很过分,因为在有些链表的实现中,会创建一个虚拟的链表头,并不是一个实际的链表结点。这样要删除的结点就不可能是链表的头结点了。当然,在面试中,我们可以把这些假设和面试官交流。这样,面试官还是会觉得我们考虑问题很周到的。

时间: 2024-07-29 21:23:06

33.在O(1)时间删除链表结点的相关文章

面试题18(一):在O(1)时间删除链表结点

// 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点.链表结点与函数的定义如下: // struct ListNode{ // int m_nValue; // ListNode* m_pNext; // }; // void deleteNode(ListNode** pListHead,ListNode* pToBeDeleted); 解题思路: 这是目前为止,唯一一道,我不看书就知道怎么做的题. 正

【Java】 剑指offer(17) 在O(1)时间删除链表结点

本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 思路 通常那样从头开始查找删除需要的时间为O(n),要在O(1)时间删除某结点,可以这样实现:设待删除结点i的下一个结点为j,把j的值复制到i,再把i的指针指向j的下一个结点,最后删除j,效果就相当于删除j. 注意特殊情况:1.当待删除结点i为尾结点时,无下一个结点,则只能从头到尾顺序遍历:2.当链

剑指Offer对答如流系列 - 在O(1)时间删除链表结点

面试题17:在O(1)时间删除链表结点 问题描述 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 链表结构 public class Node{ int val; Node next; public Node(int value,Node next) { val=value; next=next; } } 问题分析 曾经未碰到这道题之前,删除链表的节点,用的方法非常原始.基础(代码如下),很明显这种原始的方式带来的时间复杂度为O(n) //从链表中删除index(0~

《剑指offer》第十八题:在O(1)时间删除链表结点

// 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点. #include <cstdio> #include "List.h" void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if (pListHead == nullptr || pToBeDeleted == nullptr) return; //多个

在O(1)时间删除链表结点

题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 链表结点与函数的定义如下: struct ListNode { int m_nValue; ListNode* m_pNext; }; void DeleteNode(ListNode** pListHead,ListNode* pToBeDeleted); 思路:我们可以很方便的得到要删除的结点的下一结点,如果我们把下一个结点的内容复制到需要删除的结点上覆盖原有的内容,再把下一个结点删除.就相当于把当前需要删除的

在O(1)时间删除链表结点——剑指offer

题目:在给定单链表的头结点指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 代码: 1 #include <stdio.h> 2 #include "malloc.h" 3 struct ListNode 4 { 5 int value; 6 ListNode *next; 7 }; 8 //尾插法创建链表 9 ListNode *createList() 10 { 11 ListNode *H = (ListNode *)malloc(sizeof(ListNo

在O(1)时间删除链表结点——13

给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 因为要求时间复杂度为O(1),所以肯定不能遍历链表,那样的话时间复杂度就是O(N)了:可以想到,其实要求删除该结点,真正的目的并不是要将结点的数据包括结点所占的内存都给删除,只是想让数据消失就可以了,至于结点,除去任意一个结点所占的空间都是OK的: 所以,这里换一种思路,若要删除指定的结点,一般需要将前一个结点的next指针指向要删除结点的下一个结点,这样要删除的结点就可以脱离链表而被删除了,但这里关键就是即是单链表没有

O(1)时间删除链表结点

题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点.链表结点与函数的定义如下: typedef struct ListNode { int val; struct ListNode *p_next; }NODE, *PNODE; void delete_node(PNODE *pListHead, PNODE pToDeleted); 一般来说,我们拿到一个删除链表的某个结点的题目,最常规的解法无疑是从链表的头结点开始顺序遍历,依次查找要删除的结点,找到被删除结点的前

在O(1)时间删除链表结点

给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点.链表结点与函数定义如下: struct ListNode { int m_nValue; ListNode* m_pNext; }; code: //把待删结点后面一个结点的值赋给待删结点,然后把待删结点next指针指向下下个结点,然后删除下个结点, 达到和删除待删结点一样的效果. void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if (!pL