参考:五大板块(4)——链表
作者:丶PURSUING
发布时间: 2021-02-15 09:33:29
网址:https://blog.csdn.net/weixin_44742824/article/details/114981905

目录

  • 一、对比链表与数组
    • 同样是存放一串数据,链表与数组的区别在哪里?
    • 链表方便增删
  • 二、链表的静态创建
    • 链表的动态遍历:统计节点个数与查找节点
  • 三、插入节点与删除节点
    • 从指定节点的后方插入新节点
    • 在指定节点前方插入新节点
    • 删除指定节点
  • 四、链表的动态创建
    • 头插法创建链表
    • 尾插法创建链表

一、对比链表与数组

同样是存放一串数据,链表与数组的区别在哪里?

数组是申请连续的地址存放数据,在增加或删除某一元素不方便。

而链表可以很好地解决这个问题。

链表方便增删

大致思路:

  • 增加节点

  • 删除节点

二、链表的静态创建

#include <stdio.h>struct Test
{int data;struct Test *next;
};int main()
{struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};t1.next = &t2;//t1的指针指向了t2的地址t2.next = &t3;//t1.next是一个结构体指针,访问里面的data自然要用->printf("%d %d %d\n",t1.data,t1.next->data,t1.next->next->data);return 0;
}

链表的动态遍历:统计节点个数与查找节点

#include <stdio.h>struct Test
{int data;struct Test *next;
};//遍历链表,把节点数据打印出来
void printLink(struct Test *head)
{int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}
}
//统计链表节点个数
void getNodeNum(struct Test *head)
{int cnt = 0;struct Test *p = head;while(p != NULL){cnt++;p = p->next;}printf("链表节点的个数是:%d\n",cnt);
}//找节点
void findNode(struct Test *head,int data)
{struct Test *p = head;while(p != NULL){if(p->data == data){printf("找到了\n");return;//直接退出子函数,返回main函数}p = p->next;}printf("没找到\n");
}int main()
{struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};t1.next = &t2;//t1的指针指向了t2的地址t2.next = &t3;printLink(&t1);getNodeNum(&t1);findNode(&t1,2);return 0;
}

结果:

1 2 3 链表节点的个数是:3
找到了
  • 1
  • 2

要重点理解的是:p = p->next
指针p指向了下一个结构体的地址,p->next中存放的正是下一个链表节点的地址。
p本身是一个结构体指针,所以用->访问成员next.

三、插入节点与删除节点

从指定节点的后方插入新节点

思路:
(1)找到指定节点
(2)把指定节点的的next指向new节点的地址
(3)new节点的next指向下一个节点

靠,真拗口,看图!

举例:要从链表1 2 3 4 中,在 2 后插入 5 。

#include <stdio.h>struct Test
{int data;struct Test *next;
};void addBehind(struct Test *head,int data,struct Test *new)
{struct Test *p = head;while(p != NULL){if(data == p->data){new->next = p->next;//先连新节点的后面p->next = new;      //再连新节点的前面return;}p = p->next;}
}void printLink(struct Test *head)
{int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}int main()
{struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};struct Test t4 ={4,NULL};t1.next = &t2;//t1的指针指向了t2的地址t2.next = &t3;t3.next = &t4;struct Test new ={5,NULL};addBehind(&t1,2,&new);printLink(&t1);return 0;
}

结果:

1 2 5 3 4
  • 1

思考一下,为什么上面要传入结构体new的地址?

像下图一样修改,传入的是结构体变量new,然后p->next再指向new的地址不就行啦?还不是一样把地址串了起来。

void addBehind(struct Test *head,int data,struct Test new)
{struct Test *p = head;while(p != NULL){if(data == p->data){new.next = p->next;p->next = &new;//形参函数结束就释放了 p->next指向这个位置会发生断错误return;}p = p->next;}
}addBehind(&t1,2,new);

结果是:段错误

Segmentation fault
  • 1

为啥?

因为上述中new只是子函数的一个形式参数罢了,地址空间是临时分配,当函数调用结束空间回收,你让一个指针p->next指向这里,必然导致段错误!

在指定节点前方插入新节点

第一种情况:不是1之前插入,链表头未发生改变

第二种情况:是在1之前插入,链表头发生改变

举个栗子:(1)要从链表1 2 3 4 中,在 3 前插入 5 。

#include <stdio.h>struct Test
{int data;struct Test *next;
};struct Test *addInfront(struct Test *head,int data,struct Test *new)
{struct Test *p = head;if(data == head->data){new->next = head;    //先连新节点的后面head = new;return head;}while(p->next != NULL){if(data == p->next->data){new->next = p->next;//先连新节点的后面p->next = new;      //再连新节点的前面return head;}p = p->next;//让链表遍历起来}
}void printLink(struct Test *head)
{int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}int main()
{struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};struct Test t4 ={4,NULL};t1.next = &t2;//t1的指针指向了t2的地址t2.next = &t3;t3.next = &t4;struct Test new ={5,NULL};struct Test *head = &t1;head = addInfront(head,3,&new);printLink(head);return 0;
}

结果:

1 2 5 3 4
  • 1

(2)更改程序,在1之前插入5,结果:

5 1 2 3 4
  • 1

删除指定节点


删除的是头节点时,还要注意新头的替换

举例:删除 1 2 3 4中的 1

#include <stdio.h>struct Test
{int data;struct Test *next;
};struct Test *deNode(struct Test *head,int data)
{struct Test *p = head;if(data == head->data){head = head->next;return head;}while(p->next != NULL){if(data == p->next->data){p->next = p->next->next;return head;}p = p->next;}
}void printLink(struct Test *head)
{int i;struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}int main()
{struct Test t1 ={1,NULL};struct Test t2 ={2,NULL};struct Test t3 ={3,NULL};struct Test t4 ={4,NULL};t1.next = &t2;//t1的指针指向了t2的地址t2.next = &t3;t3.next = &t4;struct Test *head = &t1;head = deNode(head,1);printLink(head);return 0;
}

结果:

2 3 4
  • 1

删除 1 2 3 4中的4,结果:

1 2 3
  • 1

四、链表的动态创建

头插法创建链表

头一直是在变化的
关键步骤:

new->next = head;//new直接指向原来的链表头
head = new;//赋予新的链表头

实际例子:

运用头插法创建链表,直接输入数据自动串成链表,想要结束时,输入数据999.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct test
{int data;struct test *next;
}test,*ptest;void printLink(ptest head)
{int i;ptest p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}ptest insertHead(ptest head,ptest new)
{if(head == NULL){head = new;}else{new->next = head;//先连新节点的后面   new往前拱head = new;      //再连新节点的前面   new变成新头(头插)}return head;}ptest creatLink(ptest head)
{ptest new;while(1){new = (ptest)malloc(sizeof(test));printf("请输入新的节点,输入999结束输入\n");scanf("%d",&new->data);if(new->data == 999){free(new);new = NULL;return head;}head = insertHead(head,new);}
}int main()
{ptest head = NULL;head = creatLink(head);printLink(head);return 0;
}

结果:

请输入新的节点,输入999结束输入
3
请输入新的节点,输入999结束输入
4
请输入新的节点,输入999结束输入
5
请输入新的节点,输入999结束输入
6
请输入新的节点,输入999结束输入
999
6 5 4 3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

尾插法创建链表

关键步骤:

(1)遍历找到链表的尾部

while(p->next != NULL){p = p->next;
}
  • 1
  • 2
  • 3

(2)在尾部添加new

p->next = new;
  • 1

实际例子:

运用尾插法创建链表,直接输入数据自动串成链表,想要结束时,输入数据999.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct test
{int data;struct test *next;
}test,*ptest;void printLink(ptest head)
{int i;ptest p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}ptest insertTail(ptest head,ptest new)
{ptest p = head;if(p == NULL){head = new;return head;//没有此句段错误}while(p->next != NULL){p = p->next;//遍历找到尾巴}p->next = new;//new跟在屁股后面(尾插)return head;
}ptest creatLink(ptest head)
{ptest new;while(1){new = (ptest)malloc(sizeof(test));printf("请输入新的节点,输入999结束输入\n");scanf("%d",&new->data);if(new->data == 999){free(new);new = NULL;return head;}head = insertTail(head,new);}
}int main()
{ptest head = NULL;head = creatLink(head);printLink(head);return 0;
}

结果:

请输入新的节点,输入999结束输入
3
请输入新的节点,输入999结束输入
4
请输入新的节点,输入999结束输入
5
请输入新的节点,输入999结束输入
999
3 4 5
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

思考:当上面的inserTail函数更改为如下,会发生什么?

ptest insertTail(ptest head,ptest new)
{ptest p = head;if(head == NULL){head = new;}else{p->next = new;}return head;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

结果:可以发现无论怎样输入链表都只有第一个和最后一个数据

请输入新的节点,输入999结束输入
1
请输入新的节点,输入999结束输入
3
请输入新的节点,输入999结束输入
5
请输入新的节点,输入999结束输入
6
请输入新的节点,输入999结束输入
8
请输入新的节点,输入999结束输入
999
1 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

那是因为:使用尾插法,链表头一直未改变。然而在每一次的循环中,p->next都指向new,即为每次头都指向new。到最后链表中自然只有头和最新的new啦。

五大板块(4)——链表相关推荐

  1. 五大板块(5)——字符串

    参考:五大板块(5)--字符串 作者:丶PURSUING 发布时间: 2021-03-18 16:03:48 网址:https://blog.csdn.net/weixin_44742824/arti ...

  2. 五大板块(3)—— 结构体

    参考:五大板块(3)-- 结构体 作者:丶PURSUING 发布时间: 2021-03-18 16:02:43 网址:https://blog.csdn.net/weixin_44742824/art ...

  3. 五大板块(2)—— 指针

    参考:五大板块(2)-- 指针 作者:丶PURSUING 发布时间: 2021-03-18 16:01:22 网址:https://blog.csdn.net/weixin_44742824/arti ...

  4. 五大板块(1)—— 数组的定义,赋值与应用

    参考:五大板块(1)-- 数组的定义,赋值与应用 作者:丶PURSUING 发布时间: 2021-03-18 16:00:05 网址:https://blog.csdn.net/weixin_4474 ...

  5. 119只股连跌四周 五大板块成重灾区

    http://www.sina.com.cn 2007年09月25日 08:30 中国证券网-上海证券报 ⊙东吴证券 邓文渊 7月份以来股指连续发力上攻,如今沪指已经跨越5400点.时近中秋国庆,虽然 ...

  6. 5G | 5G新基建最新进展及投资机会【包含五大板块】

    /*********************************************************** 博主github:https://github.com/MichaelBeec ...

  7. Java--敲重点!JDK1.8 HashMap特性及底层数组+单链表+红黑树知识(建议收藏)

    ❤️‍大家好,我是贾斯汀!❤️‍ 学习目录 学习背景 HashMap特性 HashMap添加元素四步曲 前奏:HashMap如何添加一个元素? 第一步曲:根据key得到hashCode值 第二步曲:根 ...

  8. 群硕入列FoodTalks优质供应商地图数字化板块

    2022年6月28日,FoodTalks正式发布食品供应商地图,作为FBIF首席战略技术合作伙伴,群硕软件荣耀上榜! 这次发布的供应商地图覆盖配料.包装.营销等20+大类.100+细分类别.1000家 ...

  9. 60分钟入门PyTorch,官方教程手把手教你训练第一个深度学习模型(附链接)

    来源:机器之心 本文约800字,建议阅读5分钟. 本文介绍了官方教程入门PyTorch的技巧训练. 近期的一份调查报告显示:PyTorch 已经力压 TensorFlow 成为各大顶会的主流深度学习框 ...

最新文章

  1. 软件架构设计-五视图方法论
  2. 2011年100佳精美的WordPress免费博客模板
  3. java中的list时间排序
  4. 在预加载新闻时,怎么去掉初始化内容的显示尴尬?
  5. Doctrine官方手册 - 缓存
  6. [day17]appium之元素的定位
  7. UML各种图总结-精华
  8. 简单报价单模板_圣诞节祝福邮件必这样写【附件参考模板BY埃马】
  9. error: No implicit Ordering defined for Any
  10. 换一种态度看程序员(转)
  11. 解决yolo+cudnn+opencv+gpu的一些问题
  12. 贪吃蛇游戏设计预习01
  13. 学前端整理的前端常用代码,希望会对大家有用
  14. VB之不能“VB6EXT.OLB”注册
  15. 【转】WIFI-Direct(Wifi直连)、AirPlay、DLAN、Miracast功能介绍
  16. __init__在python中的用法_如何打“我爱你”的摩斯密码
  17. Excel工作表保护忘记密码解决方法(.xls和.xlsx)
  18. Linux sdkman使用方法
  19. org.apache.jasper.el.ELContextImpl cannot be cast to org.apache.jasper.el.ELContextImpl
  20. 伽马校正(gamma correction)学习笔记

热门文章

  1. 一张图学习常见this的指向
  2. abstract class 和 interface 区别
  3. Android SimpleAdapter的参数
  4. 【转】 .NET 打印水晶报表(CrystalReport)时,出现“查询引擎错误 C:/DO...
  5. [AX]AX2012开发新特性-全文索引
  6. SQL里的SWITCH分支语句
  7. hadoop等的下载地址
  8. 中国智能高清视频监控未来发展趋势
  9. 结构体指针需要申请指针内存,结构体对象不需要申请对象内存
  10. 内存颗粒位宽和容量_SDRAM的逻辑Bank与芯片容量表示方法