如何在O(1)的时间里删除单链表的结点

题目是这样的:给你一个单链表的表头,再给你其中某个结点的指针,要你删除这个结点,条件是你的程序必须在O(1)的时间内完成删除。

由于有的同学对链表还不是很熟悉,本文尽量描述的通俗易懂,老鸟请直接跳过前面一大段。

链表结构如下:

[cpp] view plaincopyprint?

  1. struct node
  2. {
  3. int val;
  4. node* next;
  5. };

题目不是很难,很快就能想到好办法:)

首先回顾一下普通的删除方法,首先通过表头,找到待删除结点(设为B)的前一个结点(设为A),将A的指向改一下就行,然后删除掉B结点就行了。要删除的结点一定要delete掉,这不仅是个好习惯,而且能避免将来项目中可能造成的内存泄露的严重问题。

[cpp] view plaincopyprint?

  1. void DeleteNode_On(node *LinkList, node *p)
  2. {
  3. printf("delete:%d\n", p->val);
  4. node *s = LinkList;
  5. while(s->next != p)
  6. s = s->next;
  7. s->next = s->next->next;
  8. delete(p);
  9. }

这个算法主要耗时在于查找前一个结点,所以是O(n)的算法。

那么既然要求是O(1),显然不能再去for一遍了,联想到数组的删除,这个问题就比较好解决了。

首先我们很容易就能得到待删除结点,即B结点的后一个结点C,然后将C的值赋值给B结点的值,相当于数组删除时候的覆盖,现在B结点和C结点一模一样了,接下来就相当简单了吧,我们不删B,直接利用B删掉C就行了,方法简单,时间O(1)。

但是仔细想想这个算法有个很明显的缺陷,如果待删除结点是最后一个结点呢?这个时候似乎没有什么好的解决办法,只能老老实实的O(n)了。现在我们来看看平均时间复杂度:

符合题目要求。

[cpp] view plaincopyprint?

  1. void DeleteNode_O1(node *LinkList, node *p)
  2. {
  3. printf("delete:%d\n", p->val);
  4. if(p->next != NULL) //如果p不是末尾结点, 则让后一个结点覆盖掉p, 然后删除后一个结点
  5. {
  6. p->val = p->next->val;
  7. node *tmp = p->next;
  8. p->next = p->next->next;
  9. delete(tmp);
  10. }
  11. else // 如果p是末尾结点, 则找到p的前一个结点然后正常删除
  12. {
  13. node *s = LinkList;
  14. while(s->next != p)
  15. s = s->next;
  16. s->next = s->next->next;
  17. delete(p);
  18. }
  19. }

最后附上完整测试代码:

[cpp] view plaincopyprint?

    1. #include<iostream>
    2. using namespace std;
    3. struct node
    4. {
    5. int val;
    6. node* next;
    7. };
    8. void CreateLinkList(node *LinkList)
    9. {
    10. node *s = LinkList;
    11. for(int i = 0; i < 10; i++)
    12. {
    13. node *t = new node;
    14. t->val = i;
    15. t->next = NULL;
    16. s = s->next = t;
    17. }
    18. }
    19. void ShowLinkList(node * LinkList)
    20. {
    21. node *s = LinkList->next;
    22. while(s = s->next)
    23. printf("%d ", s->val);
    24. putchar(10);
    25. }
    26. void DeleteNode_On(node *LinkList, node *p)
    27. {
    28. printf("delete:%d\n", p->val);
    29. node *s = LinkList;
    30. while(s->next != p)
    31. s = s->next;
    32. s->next = s->next->next;
    33. delete(p);
    34. }
    35. void DeleteNode_O1(node *LinkList, node *p)
    36. {
    37. printf("delete:%d\n", p->val);
    38. if(p->next != NULL) //如果p不是末尾结点, 则让后一个结点覆盖掉p, 然后删除后一个结点
    39. {
    40. p->val = p->next->val;
    41. node *tmp = p->next;
    42. p->next = p->next->next;
    43. delete(tmp);
    44. }
    45. else // 如果p是末尾结点, 则找到p的前一个结点然后正常删除
    46. {
    47. node *s = LinkList;
    48. while(s->next != p)
    49. s = s->next;
    50. s->next = s->next->next;
    51. delete(p);
    52. }
    53. }
    54. int main()
    55. {
    56. node *LinkList = new node;
    57. CreateLinkList(LinkList);
    58. ShowLinkList(LinkList);
    59. node *p = LinkList->next;
    60. for(int i = 0; i < 3; i++)
    61. p = p->next;
    62. DeleteNode_On(LinkList, p);
    63. ShowLinkList(LinkList);
    64. p = LinkList->next;
    65. for(int i = 0; i < 8; i++)
    66. p = p->next;
    67. DeleteNode_O1(LinkList, p);
    68. ShowLinkList(LinkList);
    69. p = LinkList->next;
    70. for(int i = 0; i < 4; i++)
    71. p = p->next;
    72. DeleteNode_O1(LinkList, p);
    73. ShowLinkList(LinkList);
    74. getchar();
    75. return 0;
    76. }

如何在O(1)的时间里删除单链表的结点

时间: 2024-10-26 00:21:09

如何在O(1)的时间里删除单链表的结点的相关文章

[算法浅析] 如何在O(1)的时间里删除单链表的结点

题目是这样的:给你一个单链表的表头,再给你其中某个结点的指针,要你删除这个结点,条件是你的程序必须在O(1)的时间内完成删除. 由于有的同学对链表还不是很熟悉,本文尽量描述的通俗易懂,老鸟请直接跳过前面一大段. 链表结构如下: struct node { int val; node* next; }; 题目不是很难,很快就能想到好办法:) 首先回顾一下普通的删除方法,首先通过表头,找到待删除结点(设为B)的前一个结点(设为A),将A的指向改一下就行,然后删除掉B结点就行了.要删除的结点一定要de

删除单链表某个结点(Java版)

题目:删除带头结点的单链表L中的结点p,p不是最后一个结点,要求时间复杂度为O(1). 解题思路: 如果不要求时间复杂度为O(1),我们可以找出p的前驱结点,然后很容易就删除p. 现在要求时间复杂度为O(1),因为p不是最后一个结点,知道结点p我们可以删除p的后继结点,那么我们可以把p的后继结点元素的值赋给p结点元素的值. ADT定义: //单链表的结点类 class LNode{ //为了简化访问单链表,结点中的数据项的访问权限都设为public public int data; public

删除单链表某个结点

题目:删除带头结点的单链表L中的结点p,p不是最后一个结点,要求时间复杂度为O(1). 解题思路: 如果不要求时间复杂度为O(1),我们可以找出p的前驱结点,然后很容易就删除p. 现在要求时间复杂度为O(1),因为p不是最后一个结点,知道结点p我们可以删除p的后继结点,那么我们可以把p的后继结点元素的值赋给p结点元素的值. ADT定义如下 #define ElemType int typedef struct LNode{ ElemType data; LNode *next; }LNode,*

删除单链表指定结点出现段错误?

起初我是这么写的: struct node * deletenode(struct node *head,int b){ struct node *pre,*cur; pre=NULL; cur=head; if(head&&head->data==b){ head=head->next; free(cur); } else{ while(cur->data!=b){ pre=cur; cur=cur->next; } pre->next=cur->ne

删除单链表中重复的结点

先批评下<<数据结构实验教程(C语言版) 清华大学出版社 :王国钧主编>>这本书,CTMD,错误真多! *删除单链表重复结点 算法-:第一个跟后面比较,同则删除;第二个...第n个; 采用 *算法二:先排序, 将排好后的相同结点删除*/ void delSameNodeDLink(LinkList h) {//内外循环 LinkList p1, p2, p3; p1 = h; while (p1->next) { p2 = p1->next; //1 p3 = p2-&

剑指offer (13) O(1)时间删除单链表结点

单链表删除结点操作: 方法一.  从链表的第一个结点开始遍历,顺序遍历到需删除结点的前一个结点,然后调整指针指向  T(n) = O(n) 方法二.  将 需删除结点i的下一个结点j(如果存在) 的值赋值给 需删除结点i,然后 删除结点j,这就相当于删除了结点i  T(n) = O(1) 需要注意以下几点: 1. 如果待删除结点为单链表尾结点,此时 其后无后继结点,这时需要 从链表开头开始 顺序遍历查找 待删除结点的前驱结点 2. 如果单链表就只有一个结点,且需要删除头结点,此时 需要修改链表头

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

// 在O(1)时间内删除单链表结点 /* 思考: 很显然链表是一个节点地址不连续的存储结构 删除节点一般很容易会想到是修改p节点的前一个节点的next为p->next 然而除非是双向链表,否则无法在常量级的时间里找到p的前节点 转变思路: 既然改变不了p前节点的next 只能在p 本身动手脚 那可以考虑修改p->data 使得p->data的值为p->next->data的值,同样可以达到效果 */ #include <iostream> #include &l

两种删除单链表结点的实现,一种O(n),一种O(1)

常规的删除单链表结点的做法是挨个查找到该结点的前一个结点,然后去掉要删除的这个结点,回收内存.这个需要O(n)的时间. 有一种比较快速的删除掉链表指定一个节点的方法,就是把下一个结点的内容复制到当前这个结点,然后把下一次结点删除掉,这个需要考虑当要删除的结点是最后一个结点的情况. 如果刚好是最后一个结点,则需要O(n)的时间,如果不是最后一个结点,可以在O(1)时间内完成删除操作. 1 // 2 // main.c 3 // SingleListDeleteNode 4 // 5 // Crea

时间复杂度分别为 O(n)和 O(1)的删除单链表结点的方法

有一个单链表,提供了头指针和一个结点指针,设计一个函数,在 O(1)时间内删除该结点指针指向的结点. 众所周知,链表无法随机存储,只能从头到尾去遍历整个链表,遇到目标节点之后删除之,这是最常规的思路和做法. 如图所示,删除结点 i,那么只需找到 i 的前驱 h,然后连 h 到 j,再销毁i 即可.虽然可以安全的删除 i 结点,但是是顺序查找找到 i,之后删除,时间复杂度是 O(n)级别的.具体做法就是:顺序查找整个单链表,找到要删除结点 i 的直接前驱 h,把 h额 next 指向i 的 nex