单链表与双链表实现

简介: 单链表与双链表实现

一、链表概念

       链表是什么?链表是⼀种物理存储结构上⾮连续、⾮顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

       简单理解一下便是:链表可以近似的看作成火车,用节点一个一个串起来,需要操作时运用节点进行操作。

       其特点如下:

       链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:

    一个是存储数据元素的数据域

    另一个是存储下一个节点地址的指针域

         链表可分为以下几类:

带头 不带头
单向

双向

循环 不循环

       一共为8种组合(2*2*2),可用下图表示:

        虽然有这么多的链表的结构,但是我们实际中最常⽤还是两种结构: 单向不带头不循环链表(单链表)双向带头循环链表(双链表)。

       原因如下:

       1. ⽆头单向⾮循环链表:结构简单,⼀般不会单独⽤来存数据。实际中更多是作为其他数据结构的⼦结构。

       2. 带头双向循环链表:结构最复杂,⼀般⽤在单独存储数据。实际中使⽤的链表数据结构,都 是带头双向循环链表。另外这个结构虽然结构复杂,但是使⽤代码实现以后会发现结构会带 来很多优势,实现反⽽简单了。

二、单链表的实现

       我们在实现链表时其实现目的与顺序表类似,无外乎为:“增删查改”四大功能。

       2.1 定义结点

1. typedef int SLTDataType;//使用目的:方便更改数据类型
2. typedef struct SListNode
3. {
4.  struct SListNode* next;
5.  SLTDataType date;
6. }SLTNode;

      注意:在定义节点(写成此结点也无所谓)时不要写成:SLTNode* next;因为编译器把结构体读完时才能达成重写条件。

       2.2 具体实现功能如下:

1. void SLTPrint(SLTNode* phead);
2. 
3. //头部插入删除/尾部插入删除
4. void SLTPushBack(SLTNode** pphead, SLTDataType x);
5. void SLTPushFront(SLTNode** pphead, SLTDataType x);
6. void SLTPopBack(SLTNode** pphead);
7. void SLTPopFront(SLTNode** pphead);
8. 
9. //查找
10. SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
11. //在指定位置之前插入数据
12. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
13. //删除pos节点
14. void SLTErase(SLTNode** pphead, SLTNode* pos);
15. //在指定位置之后插入数据
16. void SLTInsertAfter(SLTNode* pos, SLTDataType x);
17. //删除pos之后的节点
18. void SLTEraseAfter(SLTNode* pos);
19. //销毁链表
20. void SListDesTroy(SLTNode** pphead);

       接下来会带领大家一个一个实现。

       2.3 单链表打印

1. void SLTPrint(SLTNode* phead)
2. {
3.  SLTNode* p = p;
4.  while (p)
5.  {
6.    printf("%d-> ", p->date);
7.  }
8.  printf("NULL\n");
9. }

       在对链表进行打印时,我们不能像打印顺序表那样那样用for循环进行遍历, 因为链表在内存中并不是像顺序表线性存放,而是靠节点进行联系。在此处我们是否需要assert断言吗?大家可以稍作思考。

       答案是不需要,原因如下:若链表为空,不会进入for循环,直接打印NULL。

       2.4单链表插入

       单链表插入像顺序表一样分为:头插与尾插,接下来会一一实现。

              2.4.1 尾插
1. Node* SLTBuyNode(SLTDataType x)
2. {
3.  SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
4.  if (node == NULL)
5.  {
6.    return -1;
7.  }
8.  node->date = x;
9.  node->next = NULL;
10.   return node;
11. }
12. void SLTPushBack(SLTNode** pphead, SLTDataType x)//此处可猜测一下为什么使用二级指针
13. {
14.   assert(*pphead);
15.   SLTNode* newnode = SLTBuyNode(x);//因创建新节点方法会被多次使用,于是便单独封装出来
16.   if (*pphead == NULL)
17.   {
18.     *pphead = pphead;
19.   }
20.   else
21.   {
22.     SLTNode* p = *pphead;
23.     while (p)
24.     {
25.       p = p->next;
26.     }
27.     p->next = newnode;
28.   }
29. }

       在进行尾插时要注意以下几点 :

       首先,要使用二级指针来接收。在进行测试时发现使用一级指针无法改变实参,要改变实参只能传地址,一级指针传地址要用二级指针来接收。

       其次,要考虑头节点为零的情况。在头节点为空时,新开辟的节点便是我们要插入的节点。

       最后,为了使以后方便我们便创建了一个方法,需要使用使直接调用即可。

               2.4.2头插
1. void SLTPushFront(SLTNode** pphead, SLTDataType x)
2. {
3.  assert(*pphead);
4.  SLTNode* newnode = SLTBuyNode(x);
5.  newnode->next = *pphead;
6.  *pphead = newnode;
7. }

          头插的代码相较于尾插能简单一点,只需要将新开辟出的节点的指向改成头节点,将头节点改为新开辟的节点即可。

       2.5 单链表删除

       和插入一样,单链表的删除同样分为头删和尾删。

               2.5.1 尾删
1. void SLTPopBack(SLTNode** pphead)
2. {
3.  assert(pphead && *pphead);
4.  if ((*pphead)->next == NULL)
5.  {
6.    free(*pphead);
7.    *pphead = NULL;
8.  }
9.  else
10.   {
11.     SLTNode* p = *pphead;
12.     SLTNode* n;
13.     while (p->next)
14.     {
15.       n = p;
16.       p = p->next;
17.     }
18.     free(p);
19.     p = NULL;
20.     n->next = NULL;
21.     //法二
22.     //SLTNode* p = *pphead;
23.     //while(p->next->next)
24.     //{
25.     //  p = p->next;
26.     //}
27.     //free(p->next);
28.     //p->next = NULL;
29.   }
30. }

       写尾删注意如下:

       1.考虑到只有一个头节点。即该头节点指向空,可直接对其释放。

       2.若不为空,可使用两种方法:双指针或创造出的指针向后多指向一次。

       3.别忘记释放和置空。

               2.5.2 头删
1. void SLTPopFront(SLTNode** pphead)
2. {
3.  assert(pphead && *pphead);
4.  SLTNode* next = (*pphead)->next;
5.  free(*pphead);
6.  *pphead = next;
7. }

       以上便是头删实现无需考虑情况,因为该节点若为头节点那它所指向的即为NULL。只需定义一个指向下一位的节点即可。

       2.6 单链表关于指定节点的增与删

               2.6.1在指定位置之前插入数据
1. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
2. {
3.  assert(pphead && *pphead);
4.  assert(pos);
5.  SLTNode* newnode = SLTBuyNode(x);
6.  SLTNode* p = *pphead;
7.  if (pos == *pphead)
8.  {
9.    //调用头插
10.     SLTPushFront(pphead, x);
11.   }
12.   else
13.   {
14.     while (p->next != pos)
15.     {
16.       p = p->next;
17.     }
18.     newnode->next = pos;
19.     p->next = newnode;
20.   }
21. }

       在经行插入数据时,我们要创造头节点以及分析是否要分类讨论。

               2.6.2 在指定位置之后插入数据
1. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
2. {
3.  assert(pos);
4.  SLTNode* newnode = SLTBuyNode(x);
5.  newnode->next = pos->next;
6.  pos->next = newnode;
7. }

       这种插入代码比较简单创造出节点后即可快速完成。

               2.6.3删除pos节点
1. void SLTErase(SLTNode** pphead, SLTNode* pos)
2. {
3.  assert(pphead && *pphead);
4.  assert(pos);
5.  if (pos == *pphead)
6.  {
7.    //调用头删
8.    SLTPopFront(pphead);
9.  }
10.   else
11.   {
12.     SLTNode* p = *pphead;
13.     while (p->next != pos)
14.     {
15.       p = p->next;
16.     }
17.     p->next = pos->next;
18.     free(p->next);
19.     p->next = NULL;
20.   }
21. }

       代码与之前插入类似。

               2.6.4 删除pos之后的节点
1. void SLTEraseAfter(SLTNode* pos)
2. {
3.  assert(pos);
4.  SLTNode* p = pos->next;
5.  pos->next = p->next;
6.  free(p);
7.  p = NULL;
8. }

       2.7 销毁链表

1. void SListDesTroy(SLTNode** pphead)
2. {
3.  SLTNode* p = *pphead;
4.  SLTNode* n = *pphead;
5.  while (p)
6.  {
7.    n = p->next;
8.    free(p);
9.    p = n;
10.   }
11.   *pphead = NULL;
12. }

       好了,以上便是我们单链表的学习了,请大家休息片刻,我们随后开始双链表的学习。

三、双链表的实现

       接下来,我们来学习双链表。双链表与单链表有相同也有不同,大家可在学习中自行体会。(在该链表使用一级指针而不用二级指针后面会说明)

       3.1 双链表初始化

1. LTNode* LTInit(LTDataType x)
2. {
3.  LTNode* phead = LTBuyNode(x);
4.  return phead;
5. }
6. LTNode* LTBuyNode(LTDataType x)
7. {
8.  LTNode* node = (LTNode*)malloc(sizeof(LTNode));
9.  if (node == NULL)
10.   {
11.     return -1;
12.   }
13.   node->date = x;
14.   node->next = node;
15.   node->prve = node;
16.   return node;
17. }

       这里的LTBuyNode与上文单链表功能类似都是为了便于后面的使用。单、双链表的不同已在上文呈现,这里不过多介绍。

       3.2 功能实现

1. LTNode* LTInit(LTDataType x);//初始化
2. void LTDestroy(LTNode* phead);//销毁
3. void LTPrint(LTNode* phead);//打印
4. void LTPushBack(LTNode* phead, LTDataType x);//尾插
5. void LTPopBack(LTNode* phead);//尾删
6. 
7. void LTPushFront(LTNode* phead, LTDataType x);//头插
8. void LTPopFront(LTNode* phead);//头删
9. 
10. void LTInsert(LTNode* pos, LTDataType x);//在pos位置之后插入数据
11. void LTErase(LTNode* pos);//删除pos位置数据
12. LTNode* LTFind(LTNode* phead, LTDataType x);//查找数据

       3.3 头插与尾插

               3.3.1 尾插
1. void LTPushBack(LTNode* phead, LTDataType x)//尾插
2. {
3.  assert(phead);
4.  LTNode* newnode = LTBuyNode(x);
5.  newnode->next = phead;
6.  newnode->prve = phead->prve;
7.  phead->prve->next = newnode;//不可颠倒
8.  phead->prve = newnode;
9. }

       注意:后俩行代码位置不可交换。

               3.3.2 头插
1. void LTPushFront(LTNode* phead, LTDataType x)
2. {
3.  assert(phead);
4.  LTNode* newnode = LTBuyNode(x);
5.  newnode->next = phead->next;
6.  newnode->prve = phead;
7.  phead->next->prve = newnode;
8.  phead->next = newnode;
9. }

       注意点还是一样即:后俩行代码位置不可交换。

       3.4 头删与尾删

               3.4.1 头删
1. void LTPopFront(LTNode* phead)
2. {
3.  assert(phead && phead->next != phead);
4.  LTNode* del = phead->next;
5.  phead->next = del->prve;
6.  del->next->prve = phead;
7.  free(del);
8.  del = NULL;
9. }

       注意:断言时要加入后半句,否则会删掉哨兵位。

               3.4.2 尾删
1. void LTPopBack(LTNode* phead)
2. {
3.  assert(phead && phead->next != phead);
4.  LTNode* del = phead->prve;
5.  phead->prve = del->prve;
6.  del->prve->next = phead;
7.  free(del);
8.  del = NULL;
9. }

       注意点还是一样,断言要加入后半句。

       3.5 指定位置插入、修改数据

               3.5.1 指定位置后插入数据
1. void LTInsert(LTNode* pos, LTDataType x)
2. {
3.  assert(pos);
4.  LTNode* newnode = LTBuyNode(x);
5.  newnode->next = pos->next;
6.  newnode->prve = pos;
7.  pos->next->prve = newnode;
8.  pos->next = newnode;
9. }

      注意:最后两行代码顺序不可修改。

               3.5.2 删除pos位置数据

1. void LTErase(LTNode* pos)
2. {
3.  assert(pos);
4.  pos->next->prve = pos->prve;
5.  pos->prve->next = pos->next;
6.  free(pos);
7.  pos = NULL;
8. }

       3.6 查找数据

1. LTNode* LTFind(LTNode* phead, LTDataType x)
2. {
3.  assert(phead);
4.  LTNode* p = phead->next;
5.  while (p != phead)
6.  {
7.    if (p->date == x)
8.    {
9.      return p;
10.     }
11.     p = p->next;
12.   }
13.   return NULL;
14. }

       3.6 打印链表

1. void LTPrint(LTNode* phead)
2. {
3.  LTNode* p = phead->next;
4.  while (p!=phead)
5.  {
6.    printf("%d->", p->date);
7.    p = p->next;
8.  }
9.  printf("NULL\n");
10. }

       打印方法与单链表类似。

       3.7 销毁链表

1. void LTDestroy(LTNode* phead)
2. {
3.  LTNode* p = phead->next;
4.  LTNode* n = phead->next;
5.  while (p != phead)
6.  {
7.    n = p->next;
8.    free(p);
9.    p = n;
10.   }
11.   //此时p指向phead,phead还没销毁
12.   free(phead);
13.   phead = NULL;
14. }
15. //存在问题传入一级指针后实参不会修改为NULL,需要手动修改

       3.8 总结

       为什么使用一级指针而不用二级指针?

       1.传入数据之前,链表要进行初始化,为了保护哨兵位,使用一级指针即可,若使用二级指针不会改变哨兵位也可以使用,不过还是推荐使用一级指针。

       2. 保持接口一致性,减少记忆成本。此链表接口过多,如若一会一级指针,一会二级指针会造成记忆成本。

四、顺序表和双向链表的优缺点分析

       

不同点 顺序表 链表(单链表)
存储空间上 物理上⼀定连续 逻辑上连续,但物理上不⼀定连续
随机访问 ⽀持O(1) 不⽀持:O(N)
任意位置插⼊或者删除元素 可能需要搬移元素,效率低O(N) 只需修改指针指向
插入 动态顺序表,空间不够时需要扩 容 没有容量的概念
应用场景 元素⾼效存储+频繁访问 任意位置插⼊和删除频繁

五、代码展示

       5.1 单链表

               5.1.1slist.c
1. #define _CRT_SECURE_NO_WARNINGS 1
2. #include"slist.h"
3. void SLTPrint(SLTNode* phead)
4. {
5.  SLTNode* p = p;
6.  while (p)
7.  {
8.    printf("%d-> ", p->date);
9.  }
10.   printf("NULL\n");
11. }
12. SLTNode* SLTBuyNode(SLTDataType x)
13. {
14.   SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
15.   if (node == NULL)
16.   {
17.     return -1;
18.   }
19.   node->date = x;
20.   node->next = NULL;
21.   return node;
22. }
23. void SLTPushBack(SLTNode** pphead, SLTDataType x)//此处可猜测一下为什么使用二级指针
24. {
25.   assert(*pphead);
26.   SLTNode* newnode = SLTBuyNode(x);//因创建新节点方法会被多次使用,于是便单独封装出来
27.   if (*pphead == NULL)
28.   {
29.     *pphead = pphead;
30.   }
31.   else
32.   {
33.     SLTNode* p = *pphead;
34.     while (p)
35.     {
36.       p = p->next;
37.     }
38.     p->next = newnode;
39.   }
40. }
41. void SLTPushFront(SLTNode** pphead, SLTDataType x)
42. {
43.   assert(*pphead);
44.   SLTNode* newnode = SLTBuyNode(x);
45.   newnode->next = *pphead;
46.   *pphead = newnode;
47. }
48. void SLTPopBack(SLTNode** pphead)
49. {
50.   assert(pphead && *pphead);
51.   if ((*pphead)->next == NULL)
52.   {
53.     free(*pphead);
54.     *pphead = NULL;
55.   }
56.   else
57.   {
58.     SLTNode* p = *pphead;
59.     SLTNode* n;
60.     while (p->next)
61.     {
62.       n = p;
63.       p = p->next;
64.     }
65.     free(p);
66.     p = NULL;
67.     n->next = NULL;
68.     //法二
69.     //SLTNode* p = *pphead;
70.     //while(p->next->next)
71.     //{
72.     //  p = p->next;
73.     //}
74.     //free(p->next);
75.     //p->next = NULL;
76.   }
77. }
78. void SLTPopFront(SLTNode** pphead)
79. {
80.   assert(pphead && *pphead);
81.   SLTNode* next = (*pphead)->next;
82.   free(*pphead);
83.   *pphead = next;
84. }
85. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
86. {
87.   assert(pphead && *pphead);
88.   assert(pos);
89.   SLTNode* newnode = SLTBuyNode(x);
90.   SLTNode* p = *pphead;
91.   if (pos == *pphead)
92.   {
93.     //调用头插
94.     SLTPushFront(pphead, x);
95.   }
96.   else
97.   {
98.     while (p->next != pos)
99.     {
100.      p = p->next;
101.    }
102.    newnode->next = pos;
103.    p->next = newnode;
104.  }
105. }
106. void SLTInsertAfter(SLTNode* pos, SLTDataType x)
107. {
108.  assert(pos);
109.  SLTNode* newnode = SLTBuyNode(x);
110.  newnode->next = pos->next;//位置不可颠倒
111.  pos->next = newnode;
112. }
113. void SLTErase(SLTNode** pphead, SLTNode* pos)
114. {
115.  assert(pphead && *pphead);
116.  assert(pos);
117.  if (pos == *pphead)
118.  {
119.    //调用头删
120.    SLTPopFront(pphead);
121.  }
122.  else
123.  {
124.    SLTNode* p = *pphead;
125.    while (p->next != pos)
126.    {
127.      p = p->next;
128.    }
129.    p->next = pos->next;
130.    free(p->next);
131.    p->next = NULL;
132.  }
133. }
134. void SLTEraseAfter(SLTNode* pos)
135. {
136.  assert(pos);
137.  SLTNode* p = pos->next;
138.  pos->next = p->next;
139.  free(p);
140.  p = NULL;
141. }
142. void SListDesTroy(SLTNode** pphead)
143. {
144.  SLTNode* p = *pphead;
145.  SLTNode* n = *pphead;
146.  while (p)
147.  {
148.    n = p->next;
149.    free(p);
150.    p = n;
151.  }
152.  *pphead = NULL;
153. }
               5.1.2 slist.h
1. #pragma once
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<assert.h>
5. typedef int SLTDataType;//使用目的:方便更改数据类型
6. typedef struct SListNode
7. {
8.  struct SListNode* next;
9.  SLTDataType date;
10. }SLTNode;
11. 
12. 
13. void SLTPrint(SLTNode* phead);
14. 
15. //头部插入删除/尾部插入删除
16. void SLTPushBack(SLTNode** pphead, SLTDataType x);
17. void SLTPushFront(SLTNode** pphead, SLTDataType x);
18. void SLTPopBack(SLTNode** pphead);
19. void SLTPopFront(SLTNode** pphead);
20. 
21. //查找
22. SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
23. //在指定位置之前插入数据
24. void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
25. //删除pos节点
26. void SLTErase(SLTNode** pphead, SLTNode* pos);
27. //在指定位置之后插入数据
28. void SLTInsertAfter(SLTNode* pos, SLTDataType x);
29. //删除pos之后的节点
30. void SLTEraseAfter(SLTNode* pos);
31. //销毁链表
32. void SListDesTroy(SLTNode** pphead);

       5.2 双链表

               5.2.1 list.c
1. #define _CRT_SECURE_NO_WARNINGS 1
2. #include"list.h"
3. LTNode* LTInit(LTDataType x)
4. {
5.  LTNode* phead = LTBuyNode(x);
6.  return phead;
7. }
8. void LTDestroy(LTNode* phead)
9. {
10.   LTNode* p = phead->next;
11.   LTNode* n = phead->next;
12.   while (p != phead)
13.   {
14.     n = p->next;
15.     free(p);
16.     p = n;
17.   }
18.   //此时p指向phead,phead还没销毁
19.   free(phead);
20.   phead = NULL;
21. }
22. void LTPrint(LTNode* phead)
23. {
24.   LTNode* p = phead->next;
25.   while (p!=phead)
26.   {
27.     printf("%d->", p->date);
28.     p = p->next;
29.   }
30.   printf("NULL\n");
31. }
32. LTNode* LTBuyNode(LTDataType x)
33. {
34.   LTNode* node = (LTNode*)malloc(sizeof(LTNode));
35.   if (node == NULL)
36.   {
37.     return -1;
38.   }
39.   node->date = x;
40.   node->next = node;
41.   node->prve = node;
42.   return node;
43. }
44. void LTPushBack(LTNode* phead, LTDataType x)//尾插
45. {
46.   assert(phead);
47.   LTNode* newnode = LTBuyNode(x);
48.   newnode->next = phead;
49.   newnode->prve = phead->prve;
50.   phead->prve->next = newnode;//不可颠倒
51.   phead->prve = newnode;
52. }
53. void LTPopBack(LTNode* phead)
54. {
55.   assert(phead && phead->next != phead);
56.   LTNode* del = phead->prve;
57.   phead->prve = del->prve;
58.   del->prve->next = phead;
59.   free(del);
60.   del = NULL;
61. }
62. 
63. void LTPushFront(LTNode* phead, LTDataType x)
64. {
65.   assert(phead);
66.   LTNode* newnode = LTBuyNode(x);
67.   newnode->next = phead->next;
68.   newnode->prve = phead;
69.   phead->next->prve = newnode;
70.   phead->next = newnode;
71. }
72. void LTPopFront(LTNode* phead)
73. {
74.   assert(phead && phead->next != phead);
75.   LTNode* del = phead->next;
76.   phead->next = del->prve;
77.   del->next->prve = phead;
78.   free(del);
79.   del = NULL;
80. }
81. void LTInsert(LTNode* pos, LTDataType x)
82. {
83.   assert(pos);
84.   LTNode* newnode = LTBuyNode(x);
85.   newnode->next = pos->next;
86.   newnode->prve = pos;
87.   pos->next->prve = newnode;
88.   pos->next = newnode;
89. }
90. void LTErase(LTNode* pos)
91. {
92.   assert(pos);
93.   pos->next->prve = pos->prve;
94.   pos->prve->next = pos->next;
95.   free(pos);
96.   pos = NULL;
97. }
98. LTNode* LTFind(LTNode* phead, LTDataType x)
99. {
100.  assert(phead);
101.  LTNode* p = phead->next;
102.  while (p != phead)
103.  {
104.    if (p->date == x)
105.    {
106.      return p;
107.    }
108.    p = p->next;
109.  }
110.  return NULL;
111. }
               5.2.2 list.h
1. #pragma once
2. #include<stdio.h>
3. #include<stdlib.h>
4. #include<assert.h>
5. typedef int LTDataType;
6. typedef struct ListNode
7. {
8.  struct ListNode* next;
9.  struct ListNode* prve;
10.   LTDataType date;
11. }LTNode;
12. 
13. LTNode* LTInit(LTDataType x);//初始化
14. void LTDestroy(LTNode* phead);//销毁
15. void LTPrint(LTNode* phead);//打印
16. 
17. void LTPushBack(LTNode* phead, LTDataType x);//尾插
18. void LTPopBack(LTNode* phead);//尾删
19. 
20. void LTPushFront(LTNode* phead, LTDataType x);//头插
21. void LTPopFront(LTNode* phead);//头删
22. 
23. void LTInsert(LTNode* pos, LTDataType x);//在pos位置之后插入数据
24. void LTErase(LTNode* pos);//删除pos位置数据
25. LTNode* LTFind(LTNode* phead, LTDataType x);//查找数据


       完!

相关文章
|
1月前
|
存储 缓存 算法
链表全景:探索单链表、双向链表与循环链表【实战演练】
链表全景:探索单链表、双向链表与循环链表【实战演练】
36 3
|
1月前
|
存储
【单链表】数据结构单链表的实现
【单链表】数据结构单链表的实现
|
1月前
leetcode:203. 移除链表元素(有哨兵位的单链表和无哨兵位的单链表)
leetcode:203. 移除链表元素(有哨兵位的单链表和无哨兵位的单链表)
22 0
|
14天前
|
存储
链表入门(单链表讲)
链表入门(单链表讲)
链表入门(单链表讲)
|
2天前
|
存储
【海贼王的数据航海】链表—单链表
【海贼王的数据航海】链表—单链表
5 0
|
1月前
特殊链表(循环单链表,循环双链表,静态链表)
特殊链表(循环单链表,循环双链表,静态链表)
22 3
|
21天前
|
算法
数据结构和算法学习记录——线性表之单链表(下)-头插函数、尾删函数、头删函数、查找函数、pos位置插入&删除数据、单链表销毁
数据结构和算法学习记录——线性表之单链表(下)-头插函数、尾删函数、头删函数、查找函数、pos位置插入&删除数据、单链表销毁
20 0
|
21天前
|
存储 算法
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
数据结构和算法学习记录——线性表之单链表(上)-初始单链表及其尾插函数(顺序表缺陷、单链表优点、链表打印)
15 0
|
1月前
|
Java
DAY-1 | Java数据结构之链表:删除无头单链表中等于给定值 val 的所有节点
力扣203题解:使用时间复杂度为O(n)的思路删除链表中所有值为key的元素。引入辅助指针pre,记录cur的前一个节点,遍历链表时,若cur.val!=key,pre和cur同时前进;若cur.val==key,则pre.next=cur.next,cur继续前进,确保pre不急于跟随以处理连续相同值的情况。遍历结束后,处理头节点可能需要删除的特殊情况。
29 0
|
1月前
|
存储 算法 索引
数据结构与算法④(第二章下)链表概念+单链表的实现
数据结构与算法④(第二章下)链表概念+单链表的实现
24 0