当前位置:网站首页>C语言n番战--链表(九)
C语言n番战--链表(九)
2022-07-23 04:51:00 【阿杰在线送代码】
嵌入式之路,贵在日常点滴
---阿杰在线送代码
目录
一、对比链表与数组
同样是存放一串数据,链表与数组的区别在哪里?

数组是申请连续的地址存放数据,在增加或删除某一元素不方便。
而链表可以很好地解决这个问题。
链表方便增删
大致思路:
- 增加节点
- 删除节点
二、链表的创建之静态创建:最简单的创建
#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
找到了
要重点理解的是: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(p->data == data)
{
new->next = p->next;
p->next = new;
return;
}
p = p->next;
}
}
void printLink(struct Test *head)
{
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
思考一下,为什么上面要传入结构体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;
return;
}
p = p->next;
}
}
addBehind(&t1,2,new);
结果是:段错误
Segmentation fault
为啥?
因为上述中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)
{
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,1,&new);
printLink(head);
return 0;
}
结果:
1 2 5 3 4
删除指定节点
当删除的是头节点时,还要注意新头的替换!
举例:删除 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 2 3 4中的4,结果:
1 2 3四、链表的创建之动态创建
头插法创建链表
头一直是在变化的
关键步骤:
new->next = head;//new直接指向原来的链表头head = new;//赋予新的链表头
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct test
{
int data;
struct test *next;
}test,*ptest;
void printLink(ptest head)
{
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;
head = 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 == 0)
{
free(new);
//new = NULL;
printf("0 quit\n");
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)去到链表的尾部
while(p->next != NULL)
{
p = p->next;
}(2)在尾部添加new
p->next = 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 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;
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
思考:当上面的inserTail函数更改为如下,会发生什么?
ptest insertTail(ptest head,ptest new)
{
ptest p = head;
if(head == NULL){
head = new;
}else{
p->next = new;
}
return head;
}结果:可以发现无论怎样输入链表都只有第一个和最后一个数据
请输入新的节点,输入999结束输入
3
请输入新的节点,输入999结束输入
4
请输入新的节点,输入999结束输入
5
请输入新的节点,输入999结束输入
999
3 5
那是因为:使用尾插法,链表头一直未改变。然而在每一次的循环中,p->next都指向new,即为每次头都指向new。到最后链表中自然只有头和最新的new啦。
边栏推荐
- [unity daily bug] unity reports an unexpected character '‘
- Operator usage and scheduling process of 31 spark
- ROS2的topic pub 指令出现:Failed to populate field: ‘Vector3‘ object has no attribute ‘x:1‘错误
- CloudCompare&PCL 点云点匹配(基于点到面的距离)
- 微信小程序封装wx.request
- Reading the thesis "sentence embeddings using Siamese Bert networks"
- IO应知应会
- toco生成tflite模型
- 添加信任列表
- 20. Valid brackets
猜你喜欢

Information security is in danger, and it is urgent to control the leakage of enterprise data assets

Wechat applet package wx.request

04_ue4进阶_物理碰撞入门和发射火球

【Unity】AVPro使用踩坑,编辑器模式使用视频播放正常,打包后视频无法播放的问题

China Economic Net: "Yuan universe" is hot

Anaconda虚拟环境下安装opencv报错的问题

How Alibaba cloud resolves a domain name to another domain name

300 题 第六讲 二次型

XSSGAME小游戏(XSS学习)level1-15

PyQt5_QListWidget分页多选控件
随机推荐
Ultra Fast Deep Lane Detection with Hybrid Anchor Driven Ordinal Classification论文解读
Anaconda虚拟环境下安装opencv报错的问题
Toco generates tflite model
Comprehensive experiment of realizing private network interworking under mGRE environment
阿里云对象存储服务OSS前后联调
[unity daily bug] unity reports an unexpected character '‘
TZC 1283: simple sort - heap sort
Warning lnk4210 reports an error when writing the driver
Jmeter-记一次自动化造数引发的BeanShell写入excel实例
Linux: database connection
数据湖:从数据仓库看数据湖
C语言基础知识梳理(一)
构建人工智能产品/业务的两种策略(by Andrew Ng)
数据湖:Delta Lake介绍
JMeter record the BeanShell written into excel instance caused by an automatic data generation
交换机Exchanges
编译构建工具-bazel
记一次 .NET 某智能交通后台服务 CPU爆高分析
SQLZOO——SELECT Quiz
Kubernetes技术与架构(六)