0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

单链表学习的超详细说明(二)

电子设计 来源:电子设计 作者:电子设计 2020-12-24 17:33 次阅读

昨天跟大家分享了单链表的一些基本用法,今天接着继续和大家分享单链表的用法,今天分享完,单链表的操作就暂告一段落了,后面接着分享双链表的学习和实战!

一、单链表的遍历

1、什么叫遍历?

遍历就是把单链表中的各个节点挨个拿出来,就叫遍历。

2、如何来遍历单链表?

从头指针+头节点开始,顺着链表挂接指针依次访问链表的各个节点,取出这个节点的数据,然后再往下一个节点,直到最后一个节点,结束访问。

3、注意事项:

一是不能遗漏元素,二是不能重复、追求效率

4、实战代码演示:

1 #include <stdio.h>

2 #include <strings.h>

3 #include <stdlib.h>

4// 构建一个链表的节点

5struct node

6 {

7 int data; // 有效数据

8 struct node *pNext; // 指向下一个节点的指针

9 };

10 // 作用:创建一个链表节点

11 // 返回值:指针,指针指向我们本函数新创建的一个节点的首地址

12 struct node * create_node(int data)

13 {

14 struct node *p = (struct node *)malloc(sizeof(struct node));

15 if (NULL == p)

16 {

17 printf("malloc error.n");

18 return NULL;

19 }

20 // 清理申请到的堆内存

21 bzero(p, sizeof(struct node));

22 // 填充节点

23 p->data = data;

24 p->pNext = NULL;

25 return p;

26}

27// 计算添加了新的节点后总共有多少个节点,然后把这个数写进头节点中。

28void insert_tail(struct node *pH, struct node *new)

29{

30 int cnt = 0;

31 // 分两步来完成插入

32 // 第一步,先找到链表中最后一个节点

33 struct node *p = pH;

34 while (NULL != p->pNext)

35 {

36 p = p->pNext; // 往后走一个节点

37 cnt++;

38 }

39 // 第二步,将新节点插入到最后一个节点尾部

40 p->pNext = new;

41 pH->data = cnt + 1;

42 }

43 void insert_head(struct node *pH, struct node *new)

44 {

45 // 第1步: 新节点的next指向原来的第一个节点

46 new->pNext = pH->pNext;

47 // 第2步: 头节点的next指向新节点的地址

48 pH->pNext = new;

49 // 第3步: 头节点中的计数要加1

50 pH->data += 1;

51 }

52 // 遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来

53 void bianli(struct node*pH)

54 {

55 //pH->data // 头节点数据,不是链表的常规数据,不要算进去了

56 //struct node *p = pH; // 错误,因为头指针后面是头节点

57 struct node *p = pH->pNext; // p直接走到第一个节点

58 printf("-----------开始遍历-----------n");

59 // 是不是最后一个节点

60 while (NULL != p->pNext)

61 {

62 printf("node data: %d.n", p->data);

63 p = p->pNext;

64 // 走到下一个节点,也就是循环增量

65 }

66 printf("node data: %d.n", p->data);

67 printf("-------------完了-------------n");

68 }

69int main(void)

70{

71 // 定义头指针

72 //struct node *pHeader = NULL;

73 // 这样直接insert_tail会段错误。

74 struct node *pHeader = create_node(0);

75 insert_head(pHeader, create_node(11));

76 insert_head(pHeader, create_node(12));

77 insert_head(pHeader, create_node(13));

78 // 访问链表头节点的有效数据

79 printf("beader node data: %d.n", pHeader->data);

80 bianli(pHeader);

81 return 0;

82 }

编译结果;

1root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc flie1.c

2root@ubuntu-virtual-machine:/mnt/hgfs/day# ./a.out

3beader node data: 3.

4----------开始遍历-----------

5node data: 13.

6node data: 12.

7node data: 11.

8 -------------完了-------------

二、单链表的删除

1、如何找到要删除的节点?

通过遍历来查找节点。从头指针+头节点开始,顺着链表依次将各个节点拿出来,按照一定的方法比对,找到我们要删除的那个节点。

2、如何来删除一个节点?

(1)待删除的节点不是尾节点的情况:首先把待删除的节点的前一个节点的pNext指针指向待删除的节点的后一个节点的首地址(这样就把这个节点从链表中摘出来了),然后再将这个摘出来的节点free掉接口

(2)待删除的节点是尾节点的情况:首先把待删除的尾节点的前一个节点的pNext指针指向null(这时候就相当于原来尾节点前面的一个节点变成了新的尾节点),然后将摘出来的节点free掉。

3、实战代码演示:

1 #include <stdio.h>

2 #include <strings.h>

3 #include <stdlib.h>

4// 构建一个链表的节点

5struct node

6 {

7 int data; // 有效数据

8 struct node *pNext;

9 // 指向下一个节点的指针

10 };

11 // 作用:创建一个链表节点

12 // 返回值:指针,指针指向我们本函数新创建的一个节点的首地址

13 struct node * create_node(int data)

14 {

15 struct node *p = (struct node *)malloc(sizeof(struct node));

16 if (NULL == p)

17 {

18 printf("malloc error.n");

19 return NULL;

20 }

21 // 清理申请到的堆内存

22 bzero(p, sizeof(struct node));

23 // 填充节点

24 p->data = data;

25 p->pNext = NULL;

26 return p;

27}

28// 计算添加了新的节点后总共有多少个节点,然后把这个数写进头节点中。

29void insert_tail(struct node *pH, struct node *new)

30{

31 int cnt = 0;

32 // 分两步来完成插入

33 // 第一步,先找到链表中最后一个节点

34 struct node *p = pH;

35 while (NULL != p->pNext)

36 {

37 p = p->pNext; // 往后走一个节点

38 cnt++;

39 }

40 // 第二步,将新节点插入到最后一个节点尾部

41 p->pNext = new;

42 pH->data = cnt + 1;

43 }

44 void insert_head(struct node *pH, struct node *new)

45 {

46 // 第1步: 新节点的next指向原来的第一个节点

47 new->pNext = pH->pNext;

48 // 第2步: 头节点的next指向新节点的地址

49 pH->pNext = new;

50 // 第3步: 头节点中的计数要加1

51 pH->data += 1;

52 }

53 // 遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来

54 void bianli(struct node*pH)

55 {

56 //pH->data // 头节点数据,不是链表的常规数据,不要算进去了

57 //struct node *p = pH; // 错误,因为头指针后面是头节点

58 struct node *p = pH->pNext; // p直接走到第一个节点

59 printf("-----------开始遍历-----------n");

60 // 是不是最后一个节点

61 while (NULL != p->pNext)

62 {

63 printf("node data: %d.n", p->data);

64 p = p->pNext;

65 // 走到下一个节点,也就是循环增量

66 }

67 printf("node data: %d.n", p->data);

68 printf("-------------完了-------------n");

69 }

70 // 从链表pH中删除节点,待删除的节点的特征是数据区等于data

71 // 返回值:当找到并且成功删除了节点则返回0,当未找到节点时返回-1

72 int delete_node(struct node*pH, int data)

73 {

74// 找到这个待删除的节点,通过遍历链表来查找

75struct node *p = pH; // 用来指向当前节点

76struct node *pPrev = NULL;// 用来指向当前节点的前一个点

77while (NULL != p->pNext)// 是不是最后一个节点

78{

79 pPrev = p; // 在p走向下一个节点前先将其保存

80 p = p->pNext; // 走到下一个节点,也就是循环增量

81 // 判断这个节点是不是我们要找的那个节点

82 if (p->data == data)

83 {

84 // 找到了节点,处理这个节点

85 // 分为2种情况,一个是找到的是普通节点,另一个是找到的是尾节点

86 // 删除节点的困难点在于:通过链表的遍历依次访问各个节点,找到这个节点

87 // 后p指向了这个节点,但是要删除这个节点关键要操作前一个节点,但是这

88 // 时候已经没有指针指向前一个节点了,所以没法操作。解决方案就是增加

89 // 一个指针指向当前节点的前一个节点

90 if (NULL == p->pNext)

91 {

92 // 尾节点

93 pPrev->pNext = NULL; // 原来尾节点的前一个节点变成新尾节点

94 free(p); // 释放原来的尾节点的内存

95 }

96 else

97 {

98 // 普通节点

99 pPrev->pNext = p->pNext;

100// 要删除的节点的前一个节点和它的后一个节点相连,这样就把要删除的节点给摘出来了

101 free(p);

102 }

103 // 处理完成之后退出程序

104 return 0;

105 }

106}

107// 到这里还没找到,说明链表中没有我们想要的节点

108printf("没找到这个节点.n");

109return -1;

110}

111int main(void)

112{

113 // 定义头指针

114 //struct node *pHeader = NULL;

115 // 这样直接insert_tail会段错误。

116 struct node *pHeader = create_node(0);

117 insert_head(pHeader, create_node(11));

118 insert_head(pHeader, create_node(12));

119 insert_head(pHeader, create_node(13));

120 // 访问链表头节点的有效数据

121 printf("beader node data: %d.n", pHeader->data);

122 bianli(pHeader);

123delete_node(pHeader, 12);

124printf("------------------删除后-------------n");

125bianli(pHeader);

126 return 0;

127 }

编译结果:

1root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc flie1.c

2root@ubuntu-virtual-machine:/mnt/hgfs/day# ./a.out

3beader node data: 3.

4-----------开始遍历-----------

5node data: 13.

6node data: 12.

7node data: 11.

8------------完了-------------

9------------------删除后-------------

10-----------开始遍历-----------

11node data: 13.

12node data: 11.

13-------------完了-------------

三、链表的逆序:

1、什么叫链表的逆序?

链表的逆序又叫反向,意思就是把链表中所有的有效节点在链表中的顺序给反过来。

2、怎样实现链表的逆序?

首先遍历原链表,然后将原链表中的头指针和头节点作为新链表的头指针和头节点,原链表中的有效节点挨个依次取出来,采用头插入的方法插入新链表中即可。

3、实战代码演示:

1 #include <stdio.h>

2 #include <strings.h>

3 #include <stdlib.h>

4// 构建一个链表的节点

5struct node

6 {

7 int data;

8 // 有效数据

9 struct node *pNext; // 指向下一个节点的指针

10 };

11 // 作用:创建一个链表节点

12 // 返回值:指针,指针指向我们本函数新创建的一个节点的首地址

13 struct node * create_node(int data)

14 {

15 struct node *p = (struct node

16 *)malloc(sizeof(struct node));

17 if (NULL == p)

18 {

19 printf("malloc error.n");

20 return NULL;

21 }

22 // 清理申请到的堆内存

23 bzero(p, sizeof(struct node));

24 // 填充节点

25 p->data = data;

26 p->pNext = NULL;

27 return p;

28}

29// 计算添加了新的节点后总共有多少个节点,然后把这个数写进头节点中。

30 void insert_tail(struct node *pH, struct node *new)

31{

32 int cnt = 0;

33 // 分两步来完成插入

34 // 第一步,先找到链表中最后一个节点

35 struct node *p = pH;

36 while (NULL != p->pNext)

37 {

38 p = p->pNext; // 往后走一个节点

39 cnt++;

40 }

41 // 第二步,将新节点插入到最后一个节点尾部

42 p->pNext = new;

43 pH->data = cnt + 1;

44 }

45 void insert_head(struct node *pH, struct node *new)

46 {

47 // 第1步: 新节点的next指向原来的第一个节点

48 new->pNext = pH->pNext;

49 // 第2步: 头节点的next指向新节点的地址

50 pH->pNext = new;

51 // 第3步: 头节点中的计数要加1

52 pH->data += 1;

53 }

54 // 遍历单链表,pH为指向单链表的头指针,遍历的节点数据打印出来

55 void bianli(struct node*pH)

56 {

57 //pH->data // 头节点数据,不是链表的常规数据,不要算进去了

58 //struct node *p = pH; // 错误,因为头指针后面是头节点

59 struct node *p = pH->pNext; // p直接走到第一个节点

60 printf("-----------开始遍历-----------n");

61 // 是不是最后一个节点

62 while (NULL != p->pNext)

63 {

64 printf("node data: %d.n", p->data);

65 p = p->pNext;

66 // 走到下一个节点,也就是循环增量

67 }

68 printf("node data: %d.n", p->data);

69 printf("-------------完了-------------n");

70 }

71 // 从链表pH中删除节点,待删除的节点的特征是数据区等于data

72// 返回值:当找到并且成功删除了节点则返回0,当未找到节点时返回-1

73int delete_node(struct node*pH, int data)

74{

75// 找到这个待删除的节点,通过遍历链表来查找

76struct node *p = pH;

77 // 用来指向当前节点

78struct node *pPrev = NULL;

79// 用来指向当前节点的前一个节点

80while (NULL != p->pNext) // 是不是最后一个节点

81{

82 pPrev = p; // 在p走向下一个节点前先将其保存

83 p = p->pNext; // 走到下一个节点,也就是循环增量

84 // 判断这个节点是不是我们要找的那个节点

85 if (p->data == data)

86 {

87 // 找到了节点,处理这个节点

88 // 分为2种情况,一个是找到的是普通节点,另一个是找到的是尾节点

89 // 删除节点的困难点在于:通过链表的遍历依次访问各个节点,找到这个节点

90 // 后p指向了这个节点,但是要删除这个节点关键要操作前一个节点,但是这

91 // 时候已经没有指针指向前一个节点了,所以没法操作。解决方案就是增加

92 // 一个指针指向当前节点的前一个节点

93 if (NULL == p->pNext)

94 {

95 // 尾节点

96 pPrev->pNext = NULL; // 原来尾节点的前一个节点变成新尾节点

97 free(p); // 释放原来的尾节点的内存

98 }

99 else

100 {

101 // 普通节点

102 pPrev->pNext = p->pNext;

103 // 要删除的节点的前一个节点和它的后一个节点相连,这样就把要删除的节点给摘出来了

104 free(p);

105 }

106 // 处理完成之后退出程序

107 return 0;

108 }

109}

110// 到这里还没找到,说明链表中没有我们想要的节点

111printf("没找到这个节点.n");

112return -1;

113}

114 // 将pH指向的链表逆序

115 void reverse_linkedlist(struct node *pH)

116 {

117struct node *p = pH->pNext;

118 // pH指向头节点,p指向第1个有效节点

119struct node *pBack;

120 // 保存当前节点的后一个节点地址

121// 当链表没有有效节点或者只有一个有效节点时,逆序不用做任何操作

122if ((NULL ==p) || (NULL == p->pNext))

123 return;

124// 当链表有2个及2个以上节点时才需要真正进行逆序操作

125while (NULL != p->pNext) // 是不是最后一个节点

126{

127 // 原链表中第一个有效节点将是逆序后新链表的尾节点,尾节点的pNext指向NULL

128 pBack = p->pNext; // 保存p节点后面一个节点地址

129 if (p == pH->pNext)

130 {

131 // 原链表第一个有效节点

132 p->pNext = NULL;

133 }

134 else

135 {

136 // 原链表的非第1个有效节点

137 p->pNext = pH->pNext;

138 }

139 pH->pNext = p;

140 //p = p->pNext; // 这样已经不行了,因为p->pNext已经被改过了

141 p = pBack; // 走到下一个节点

142}

143// 循环结束后,最后一个节点仍然缺失

144insert_head(pH, p);

145}

146int main(void)

147{

148 // 定义头指针

149 //struct node *pHeader = NULL;

150 // 这样直接insert_tail会段错误。

151 struct node *pHeader = create_node(0);

152 insert_head(pHeader, create_node(11));

153 insert_head(pHeader, create_node(12));

154 insert_head(pHeader, create_node(13));

155 // 访问链表头节点的有效数据

156 printf("beader node data: %d.n", pHeader->data);

157 bianli(pHeader);

158reverse_linkedlist(pHeader);

159printf("------------------逆序后-------------n");

160bianli(pHeader);

161 return 0;

162 }

编译结果:

1root@ubuntu-virtual-machine:/mnt/hgfs/day# gcc

2 flie1.c

3root@ubuntu-virtual-machine:/mnt/hgfs/day#

4./a.out

5 beader node data: 3.

6 -----------开始遍历-----------

7 node data: 13.

8 node data: 12.

9 node data: 11.

10 -------------完了-------------

11 ------------------逆序后-------------

12 -----------开始遍历-----------

13 node data: 11.

14 node data: 12.

15 node data: 13.

16 -------------完了-------------

四、总结:

通过两天的单链表学习,让自己理解更加深刻,不过学的东西还是最后能够用到实战当中去,这样才是最后的学习方法!

每天学一点,日积月累就有质的提升!如果您觉得好,可以给关注哦,这是对我的最大鼓励哦;我会继续努力加油的

审核编辑:符乾江
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 可编程逻辑
    +关注

    关注

    7

    文章

    512

    浏览量

    43907
  • 数据结构
    +关注

    关注

    3

    文章

    564

    浏览量

    39900
收藏 人收藏

    评论

    相关推荐

    3KW工业变频器电路设计方案详细说明

    3KW工业变频器电路设计方案详细说明
    的头像 发表于 03-19 08:33 154次阅读
    3KW工业变频器电路设计方案<b class='flag-5'>详细说明</b>

    数组和链表在内存中的区别 数组和链表的优缺点

    数组和链表在内存中的区别 数组和链表的优缺点  数组和链表是常见的数据结构,用于组织和存储数据。它们在内存中的存储方式以及优缺点方面存在一些显著的差异。本文将详细探讨这些差异以及它们的
    的头像 发表于 02-21 11:30 267次阅读

    数组和链表有何区别

    数组和链表的区别,这个问题,不仅面试中经常遇到,考研的同学也得掌握才行。
    的头像 发表于 02-19 15:33 180次阅读
    数组和<b class='flag-5'>链表</b>有何区别

    数据结构:单链表的排序

    给定一个单链表的头结点head(该结点有值),长度为n的无序单链表,对其按升序排序后,返回新链表。如当输入链表 {3,1,4,5,2} 时,经升序排列后,原
    的头像 发表于 11-30 13:56 441次阅读
    数据结构:单<b class='flag-5'>链表</b>的排序

    黑金Spartan6开发板的Verilog教程详细说明

    黑金Spartan6开发板的Verilog教程详细说明
    发表于 10-11 18:02 7次下载

    电子管的代换资料详细说明

    本文档的主要内容详细介绍的是电子管的代换资料详细说明
    发表于 09-26 07:24

    KT142C-sop16语音芯片ic的串口指令详细说明_默认9600指令可设

    KT142C-sop16语音芯片ic的串口指令详细说明_默认9600指令可设
    的头像 发表于 09-07 12:00 432次阅读
    KT142C-sop16语音芯片ic的串口指令<b class='flag-5'>详细说明</b>_默认9600指令可设

    NUC505的启动方式有没有相关文档说明?

    NUC505支持多种启动方式, 每种启动的方式和流程,有没有相关文档说明? 官网没有找到, 参考文档也没有详细说明
    发表于 08-29 06:27

    C语言链表知识点(2)

    C语言链表知识点(2)
    发表于 08-22 10:38 181次阅读
    C语言<b class='flag-5'>链表</b>知识点(2)

    如何判断链表是否有环

    如何判断链表是否有环?
    发表于 08-10 17:07 474次阅读
    如何判断<b class='flag-5'>链表</b>是否有环

    链表和双链表的区别在哪里

    链表和双链表的区别 单链表的每一个节点中只有指向下一个结点的指针,不能进行回溯。 双链表的每一个节点给中既有指向下一个结点的指针,也有指向上一个结点的指针,可以快速的找到当前节点的前
    的头像 发表于 07-27 11:20 1110次阅读
    单<b class='flag-5'>链表</b>和双<b class='flag-5'>链表</b>的区别在哪里

    链表数据结构基本概念

    链表基本概念 头指针: 头指针是指链表指向第一个结点的指针,若链表有头结点,则是指向头结点的指针。 头指针具有标识作用,所以常用头指针冠以链表的名字。 无论
    的头像 发表于 07-27 11:14 615次阅读
    <b class='flag-5'>链表</b>数据结构基本概念

    开源RISC-V处理器(蜂鸟E203)学习笔记

    需要使用我分享的EDA虚拟机:IC_EDA_ALL虚拟机(丰富版)详细说明.
    的头像 发表于 06-29 10:21 2824次阅读
    开源RISC-V处理器(蜂鸟E203)<b class='flag-5'>学习</b>笔记

    C语言算法题:反转一个单向链表

    链表是编程学习的一个难点。其实,在C语言编程以及单片机裸机开发中,链表运用并不多。但是如果想提升嵌入式技能水平或收入水平,可以考虑深入嵌入式系统层面(如参与操作系统设计、深入学习新的操
    发表于 06-21 11:07 389次阅读
    C语言算法题:反转一个单向<b class='flag-5'>链表</b>

    FPGA AXI4协议学习笔记(三)

    上文FPGA IP之AXI4协议1_信号说明把AXI协议5个通道的接口信息做了说明,本文对上文说的信号进行详细说明
    的头像 发表于 05-24 15:06 722次阅读
    FPGA AXI4协议<b class='flag-5'>学习</b>笔记(三)