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

    文章

    526

    浏览量

    45307
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    41374
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    LUA例程-常用的回调函数使用说明

    详细说明LUA脚本函数功能和对应的应用实例。对于LUA脚本编程有很大的帮助和提高技能。
    发表于 11-24 16:43 0次下载

    ​多电能表RS485并联接入充电桩主板的技术说明

    本文将详细说明其接线方法、技术原理及注意事项,为您提供清晰的解决方案。
    的头像 发表于 10-31 16:54 1303次阅读
    ​多电能表RS485并联接入充电桩主板的技术<b class='flag-5'>说明</b>

    rt_object_get_information获取到的链表为空怎么解决?

    rtt启动过程,在初始化堆的时候,进入rt_object_init,调用rt_object_get_information获取到的链表为空,导致系统起不来。
    发表于 10-11 11:44

    运算放大器的使用详细说明

    运算放大器权威指南 第三版 获取完整文档资料可下载附件哦!!!! 如果内容有帮助可以关注、点赞、评论支持一下哦~
    发表于 08-01 13:51

    表面贴装 0402 硅突变调谐变容极管 skyworksinc

    电子发烧友网为你提供()表面贴装 0402 硅突变调谐变容极管相关产品参数、数据手册,更有表面贴装 0402 硅突变调谐变容极管的引脚图、接线图、封装手册、中文资料、英文资料,
    发表于 07-11 18:33
    表面贴装 0402 硅<b class='flag-5'>超</b>突变调谐变容<b class='flag-5'>二</b>极管 skyworksinc

    表面贴装、硅突变调谐变容极管 skyworksinc

    电子发烧友网为你提供()表面贴装、硅突变调谐变容极管相关产品参数、数据手册,更有表面贴装、硅突变调谐变容极管的引脚图、接线图、封装手册、中文资料、英文资料,表面贴装、硅
    发表于 07-11 18:32
    表面贴装、硅<b class='flag-5'>超</b>突变调谐变容<b class='flag-5'>二</b>极管 skyworksinc

    磁珠与电感的对比

    本文分三部分,详细的描述了电感的定义、磁珠的定义以及对比了磁珠与电感的区别,通过举例方式详细说明了磁珠的应用场合和使用方法
    发表于 05-29 15:50

    贴片三极管上的印字与真实名称的对照表详细说明

      本文档的主要内容详细介绍的是贴片三极管上的印字与真实名称的对照表详细说明
    发表于 05-28 09:05 110次下载

    瑞芯微rv1106开发资料 rv1106数据手册 rv1106详细说明书免费下载

    瑞芯微rv1106开发资料 rv1106数据手册 rv1106详细说明书免费下载
    的头像 发表于 05-19 11:16 4266次阅读
    瑞芯微rv1106开发资料 rv1106数据手册 rv1106<b class='flag-5'>详细说明</b>书免费下载

    调试变频器详细说明

    调试变频器是一个复杂但至关重要的过程,它涉及多个参数的设定和调整,以确保变频器能够正常运行并满足特定应用需求。以下是对变频器调试的详细说明。 一、准备工作 1. 选择合适的电机功率:根据实际需求选择
    的头像 发表于 04-25 15:32 1453次阅读

    图表细说电子元器件(建议下载)

    资料介绍本文档共9章内容,以图文同页的方式细说了常用的11大类数十种电子元器件,介绍元器件的识别方法、电路符号识图信息、主要特性、重要参数、典型应用电路、检测方法、修配技术、更换操作、调整技术等相关
    发表于 04-17 17:10

    编码器常见的故障详细说明

    运行和加工精度。本文将详细说明编码器常见的故障及其排除方法,以帮助用户更好地维护和使用编码器。 一、信号输出故障 1. 无信号输出:编码器无法产生信号,上位机或控制系统接收不到任何数据,导致设备无法正常运行。这可能
    的头像 发表于 04-16 18:28 3214次阅读

    详细的反激式开关电源电路图讲解

    、双管反激、准谐振  300W-500W 准谐振、双管正激、半桥等  500W-2000W 双管正激、半桥、全桥  2000W以上 全桥  , 重点   在开关电源市场中,400W以下的电源大约
    发表于 03-27 16:30

    VirtualLab Fusion应用:透镜的设计与分析

    与设置:平台互操作性 连接建模技术:构透镜  构透镜(柱结构分析)  传播到焦点  探测器 周期性微纳米结构可用的建模技术: 作为一种严格的特征模态求解器,傅里叶模态法(也称为严格耦合波分
    发表于 03-04 10:05

    UHV系列雷电冲击电压发生器试验装置详细说明使用

    UHV系列 雷电冲击电压发生器试验装置产品详细说明
    发表于 02-21 17:55 17次下载