【转】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)。
基于前面的分析,我们不难写出下面的代码。
参考代码:
///////////////////////////////////////////////////////////////////////
// Delete a node in a list
// Input: pListHead - the head of list
// pToBeDeleted - the node to be deleted
///////////////////////////////////////////////////////////////////////
void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted)
{
if(!pListHead || !pToBeDeleted)
return;
// if pToBeDeleted is not the last node in the list
if(pToBeDeleted->m_pNext != NULL)
{
// copy data from the node next to pToBeDeleted
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nKey = pNext->m_nKey;
pToBeDeleted->m_pNext = pNext->m_pNext;
// delete the node next to the pToBeDeleted
delete pNext;
pNext = NULL;
}
// if pToBeDeleted is the last node in the list
else
{
// get the node prior to pToBeDeleted
ListNode* pNode = pListHead;
while(pNode->m_pNext != pToBeDeleted)
{
pNode = pNode->m_pNext;
}
// deleted pToBeDeleted
pNode->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}
}
值得注意的是,为了让代码看起来简洁一些,上面的代码基于两个假设:(1)给定的结点的确在链表中;(2)给定的要删除的结点不是链表的头结点。不考虑第一个假设对代码的鲁棒性是有影响的。至于第二个假设,当整个列表只有一个结点时,代码会有问题。但这个假设不算很过分,因为在有些链表的实现中,会创建一个虚拟的链表头,并不是一个实际的链表结点。这样要删除的结点就不可能是链表的头结点了。当然,在面试中,我们可以把这些假设和面试官交流。这样,面试官还是会觉得我们考虑问题很周到的。
分享到:
相关推荐
在O(1)时间删除链表结点.md
在O(1)时间删除链表结点给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点,链表结点与函数的定义如下:// 要删除的结点不是尾结点//
在链表中删除结点是编程中的一种常见操作,但是如何在O(1)时间复杂度中删除结点却是一种挑战性的任务。今天,我们将讨论一种可行的解决方案,并提供完整的代码实现。 首先,让我们来看一下链表结点的定义: ```c ...
1. 初级程序员注重算法和数据结构 2. 事先做好准备,对工作有热情 3. 面试过程放松。不要急于写代码,了解清楚所要解决的问题,多和面试官沟通,然后开始做一些整体的设计和规划。不要急于提交,自己测试几个用例避免错误...
题目要求我们删除链表中的倒数第 N 个节点。...这种方法的时间复杂度是 O(N),空间复杂度也是 O(N),其中 N 是链表的长度。在处理链表问题时,合理利用辅助数据结构如栈或队列往往能简化问题的解决。
这将使得时间复杂度降低到O(1),但会增加空间复杂度。 总结一下,查找链表中倒数第k个节点的问题主要考察了对链表数据结构的理解和操作,以及双指针算法的应用。`MyLinkList.java`文件中的实现可能包括了链表节点和...
删除操作的时间复杂度是O(n),其中n是链表的长度。 排序操作 排序操作是指对链表中的结点进行排序。排序操作可以使用不同的排序算法,例如冒泡排序、选择排序、插入排序等。排序操作的时间复杂度取决于所使用的...
通过上述方法,我们可以在 O(n) 的时间复杂度内找到并删除链表的倒数第 N 个结点。这种方法避免了两次遍历链表的过程,从而提高了算法的效率。快慢指针技巧在处理链表问题时非常实用,尤其是在需要找到特定位置的...
在O(1)时间内删除链表节点的关键在于通过重新连接链表中的指针来完成删除。当我们要删除节点i时,可以先将其下一个节点j的值复制到i,然后让i的next指针指向j的下一个节点,最后删除j。这个过程分为以下三种情况: ...
对于链表中任意位置的元素进行访问都需要从头结点开始遍历到目标位置,时间复杂度为O(n)。 ##### 1.2 二维链表的存储结构 **结点结构** 二维链表是在一维链表的基础上扩展而来,每个结点除了包含数据域外,还包括...
3. **单向循环链表**:如果知道循环链表的尾结点,可以直接删除,时间复杂度为 `O(1)`;否则,需要遍历整个链表找到前驱结点,时间复杂度为 `O(n)`。 #### 十二、删除循环链表结点的前驱 对于既无头结点也无头指针...
由于双链表没有随机访问的能力,查找特定位置或值的结点通常需要线性时间复杂度,即O(n)。 ### 知识回顾与重要考点 - 不带头结点的双链表在实现上更简洁,但初始化和操作可能会更复杂,特别是在处理链表的边界情况...
在处理动态插入和删除操作时,链表比数组更灵活,因为它们不需要预留连续的内存空间。在最小生成树问题中,链表可能被用来存储边的集合,以便在解决Prim或Kruskal算法时跟踪已选择的边。 二叉树是另一种重要的数据...
初始化操作的时间复杂度为O(1),因为这通常只是设置链表的头结点和一些基本属性。 2. `clist_destroy(CList *list)`: 这个函数用于销毁整个循环链表,包括所有的元素。在销毁过程中,会调用在`clist_init`中提供的`...
这些操作的时间复杂度均为O(1),即它们的执行时间不随链表的规模而变化。 插入结点的操作可以分为两步:首先,找到要插入结点的位置,然后将新的结点插入到该位置。插入结点的代码为`temp->next->prior = temp; ...
14. **链表插入时间复杂度**:对于单链表,在表头插入元素的时间复杂度为\(O(1)\),因为只需要改变头结点的`next`指针即可;而在表尾插入的时间复杂度为\(O(n)\),因为需要遍历整个链表找到尾部。 15. **二维数组的...
这种特性使得链表在插入和删除操作上具有优势,因为它们不需要移动大量元素。 链表主要由节点(也称为结点)组成,每个节点包含两部分:数据域,用于存储实际的数据;指针域,用于指向下一个节点。根据指针的方向,...
通过这个题目的解决,我们不仅学习了如何在链表中删除特定节点,还掌握了双指针技巧,这是一种在处理链表问题时非常实用的方法。在实际面试或工作中,理解和熟练运用这类数据结构问题的解决方案,将有助于提升我们的...
3. 六叉链表表示30个结点的六叉树,每个结点有六个子结点,但只有最后一个子结点可以链接其他结点,所以空指针数量为30 * (6 - 1) = 150个。 4. 完全二叉树的性质决定了度为0的结点(叶子结点)数量与度为2的结点...
而对于无头结点的链表,我们需要额外注意初始处理,因为没有头结点,我们需要在遍历过程中临时创建一个头结点,然后在完成逆序后将其移除。 插入排序是另一种基本的排序算法,特别适合于小规模或者部分有序的数据。...