C语言数据结构:链表的增删改查及动态创建

news/2024/11/7 22:43:41/

目录

一,链表与数组

① 定义区别

② 实现区别

二,链表遍历和计算链表中节点数量

① 链表遍历

② 计算节点数量

三,查找链表节点

四,增加节点到链表中

① 在节点后方插入

② 在节点前方插入

● 在头节点前方插入

● 在其他节点前方插入

五,删除链表中指定的节点

① 删除链表头节点

② 删除链表其它节点

六,修改链表中指定的节点

七,动态创建链表

① 头插法创建链表

② 尾插法创建链表


一,链表与数组

① 定义区别

数组:数组是在内存中连续存储的具有相同类型的一组数据的集合

      ● 数组中数的类型必须相同;

      ● 数组中数在内存中必须是连续存储的

链表:把它想象成自行车链条,由n个节点组成,可以对这些节点进行“增,删,改,查”等操作,即对数据的操作。

      ● 可以进行动态存储分配

      ● 可以在节点中定义多种数据类型

      ● 用NULL来表示空节点;

      ● 链表都有一个头指针,一般以head来表示,存放的是一个地址

      ● 链表中包含头结点其它节点两种,头结点是没有数据域的;

      ● 链表中每个节点都分为两个区域,一个数据域,一个是指针域;

② 实现区别

数组:

#include <stdio.h>int main()
{int i;int arr[] = {1,2,3,4,5,6,7,8};int len = sizeof(arr)/sizeof(arr[0]);for(i=0;i<len;i++){printf("%d ",arr[i]);}putchar('\n');printf("number=%d\n",len);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 5 6 7 8 
number=8

链表:

#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};struct Test t4 = {4,NULL};t1.next = &t2; //t1节点的下一个节点为&t2,因为struct Test *next,所以要取地址t2.next = &t3; //t2节点的下一个节点为&t3t3.next = &t4; //t3节点的下一个节点为&t4printf("%d %d %d %d\n",t1.data,t1.next->data,t1.next->next->data,t1.next->next->next->data);//此处以结构体的方式打印,用来引入链表主题return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4

二,链表遍历和计算链表中节点数量

① 链表遍历

struct Test *p = head;              //定义链表头while(p != NULL){           //如果链表头不为空printf("%d ",p->data);  //就先打印链头的值p = p->next;        //一直链接下一个数的值,不断打印,直到节点值为NULL就停止
#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;      //定义链表头while(p != NULL){           //如果链表头不为空printf("%d ",p->data);  //就先打印链头的值p = p->next;        //一直链接下一个数的值,不断打印,直到节点值为NULL就停止}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;t2.next = &t3;t3.next = &t4;printLink(&t1); //此处t1就是链头,要取链表头的地址return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4

② 计算节点数量

int cnt = 0;  //接收链表数量       struct Test *p = head;while(p != NULL){  //如果链头不为空,那么cnt++直到链尾出现NULL就停止cnt++;    p = p->next;}return cnt; //返回值取cnt
#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);   //遍历链表部分p = p->next;}putchar('\n');
}int numLinkList(struct Test *head)
{int cnt = 0;  //接收链表数量       struct Test *p = head;while(p != NULL){  //如果链头不为空,那么cnt++直到链尾出现NULL就停止cnt++;    p = p->next;}return cnt; //返回值取cnt
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};t1.next = &t2;t2.next = &t3;t3.next = &t4;printLink(&t1); //遍历链表ret = numLinkList(&t1);  //t1为链头,该函数传参就传t1的地址printf("num = %d\n",ret);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
num = 4

三,查找链表节点

int searchLinklist(struct Test *head,int n)//定义n为要查找的节点
{struct Test *p = head;while(p != NULL){if(p->data == n){   //如果头节点就是我们要查找的节点return 1;   //那么返回值为1,表示找到}p = p->next;  //否者就继续往下一个节点查找,直到找到或者节点为空就停止}return 0;        //一直没找到就返回0;
}
#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data); //遍历链表部分p = p->next;}putchar('\n');
}int searchLinklist(struct Test *head,int n)
{struct Test *p = head;while(p != NULL){if(p->data == n){   //如果头节点就是我们要查找的节点return 1;   //那么返回值为1,表示找到}p = p->next;  //否者就继续往下一个节点查找,直到找到或者节点为空就停止}return 0;        //一直没找到就返回0;
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};t1.next = &t2;t2.next = &t3;t3.next = &t4;printLink(&t1);//遍历链表ret = searchLinklist(&t1,3); //查找3这个节点if(ret == 1){    //如果返回值为1printf("have 3\n"); //就查找到3这个节点}else{printf("no 3\n");  //否者就没有3这个节点}ret = searchLinklist(&t1,7); //查找7这个节点if(ret == 1){printf("have 7\n");}else{printf("no 7\n");}return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4   //遍历出来的链表节点
have 3    //查找到有3这个节点
no 7      //没有查找到7这个节点

四,增加节点到链表中

① 在节点后方插入

逻辑分析:

● 函数中需要定义新插入的节点new,和在哪个节点后插入n;

● 遍历链表,找到要插入的节点位置;while(p != NULL){p = p->next;}

● 如果链头就是要找的那个节点:p->data == n;

● 新节点的下一个=找的那个节点的下一个:new->next = p->next

● 找的那个节点的下一个节点就是新插入的节点:p->next = new; 

#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)   //遍历链表部分
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}void searchBehindLink(struct Test *head,struct Test *new,int n)
{                                    //new为新节点,n为要在哪个节点后插入节点struct Test *p = head;while(p != NULL){    //需要遍历步骤,找到要在那个节点后方插入节点      if(p->data == n){ //如果链头的值就是要找的那个节点new->next = p->next; //新节点的下一个=找的那个节点的下一个p->next = new; //找的那个节点的下一个就是新插入的节点}p = p->next;}
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test new = {100,NULL}; //定义要插入的节点,插入的数是100t1.next = &t2;t2.next = &t3;t3.next = &t4;printLink(&t1);//遍历searchBehindLink(&t1,&new,3);//在节点3后面插入新节点100printLink(&t1);//插入新节点后在遍历一次,看是否插入成功return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4         //原先的节点数
1 2 3 100 4     //插入成功,在3节点后方插入100

② 在节点前方插入

● 在头节点前方插入

如果在头结点插入新节点,那么头节点的地址就会发生改变,需要定义指针变量来接收新的头结点地址。

#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head) //遍历链表部分
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{struct Test *p = head;if(p->data == n){  //链表头的前方插入new->next = p; //直接让新节点new->next等于现在的头就行了return new; //返回一个new}
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test new = {102,NULL}; //插入102struct Test *head = NULL;t1.next = &t2;t2.next = &t3;t3.next = &t4;head = &t1;printLink(head);//遍历链表head = searchFrontLink(head,&new,1);//返回的是new,main函数中的头发生改变,重新用head来接收printLink(head);//添加完成,再次遍历链表,验证是否插入成功return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4      //遍历链表
102 1 2 3 4  //在头节点前方插入新节点102,插入成功

● 在其他节点前方插入

struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{struct Test *p = head;while(p->next != NULL){ //此时没有在链头的前方插入,所以要定义链头的下一个不为NULLif(p->next->data == n){   //链头的下一个节点就是我们要找的节点new->next = p->next; //插入进来的新节点就是头节点的下一个p->next = new; //头节点的下一个就是新节点return p; //此时要返回到头} p = p->next;}return p; //返回头节点
}
#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{struct Test *p = head;while(p->next != NULL){ //此时没有在链头的前方插入,所以要定义链头的下一个不为NULLif(p->next->data == n){   //链头的下一个节点就是我们要找的节点new->next = p->next; //插入进来的新节点就是头节点的下一个p->next = new; //头节点的下一个就是新节点return p; //此时要返回到头} p = p->next;}return p; //返回头节点
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test new = {102,NULL}; //插入新节点102struct Test *head = NULL;t1.next = &t2;t2.next = &t3;t3.next = &t4;head = &t1;printLink(head);searchFrontLink(head,&new,3); //插在节点3的前面printLink(head);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 2 102 3 4  //在节点3的前面插入新节点102

五,删除链表中指定的节点

删除节点后,内存中会存在残留的缓存数据,需要使用free()来释放地址空间,而free()一般是需要承接malloc()动态开辟了的地址空间。

① 删除链表头节点

逻辑分析:

● 删除的是头节点,所以不需要遍历;

● 确定头节点就是我们要删除的节点:p->data == n;

● 那么新的头节点就是原来头节点的下一个节点:head = head->next;

● 释放删除节点的内存空间:free();

● 删除链头,链头地址发生改变,需要返回新的链头

#include <stdio.h>
#include <stdlib.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}struct Test *deletLink(struct Test *head,int n)
{struct Test *p = head;if(p->data == n){  //如果头节点的值就是我们要删除的节点head = head->next; //那么新的头节点就是原来头节点的下一个节点free(p); //删除了节点就要释放删除节点的内存return head;//链头被删除,头发生了改变,就要返回新链头}
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test *head = NULL;struct Test *p = (struct Test *)malloc(sizeof(struct Test));//动态开辟链头地址空间
//      t1.next = &t2;p->data = 1;//头节点里的值为1p->next = &t2;//头节点的下一个节点为t2t2.next = &t3;t3.next = &t4;//      head = &t1;head = p;printLink(head);head = deletLink(head,1);//头发生该表,需要新的链头来承接链表头printLink(head);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
2 3 4     //头结点1被删除

② 删除链表其它节点

逻辑分析:

● 遍历链表,遍历出要删除的其它节点;

● 因为不是删链头,所以循环遍历的条件为:p->next != NULL;

● 找到要删除的节点:p->next->data == n;

● 返回值为链头

#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}struct Test *deletLink(struct Test *head,int n)
{struct Test *p = head;while(p->next != NULL){  //不是删除头节点,所以定义头节点的下一个节点不为NULLif(p->next->data == n){  //如果头节点的下一个节点就是我们要删除的节点p->next = p->next->next; //那么头节点的下一个就是头节点下一个的下一个return head;}p = p->next;}
}
int main()
{int ret;struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test *head = NULL;t1.next = &t2;t2.next = &t3;t3.next = &t4;head = &t1;printLink(head);head = deletLink(head,2);printLink(head);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 3 4    //节点2被删除

六,修改链表中指定的节点

int changeLink(struct Test *head,struct Test *new,int n)
{struct Test *p = head;while(p != NULL){   //遍历if(p->data == n){ //如果头结点的data就是我们=要改变的节点np->data = new->data;//那么找到的节点数据就等于要改的数据return 1;//返回1,修改成功}p = p->next;//没有找到那个节点,就一直遍历到NULL节点停止}
}
#include <stdio.h>struct Test{int data;struct Test *next;
};void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){printf("%d ",p->data);p = p->next;}putchar('\n');
}int changeLink(struct Test *head,struct Test *new,int n)
{struct Test *p = head;while(p != NULL){   //遍历if(p->data == n){ //如果头结点的data就是我们=要改变的节点np->data = new->data;//那么找到的节点数据就等于要改的数据return 1;//返回1,修改成功}p = p->next;//没有找到那个节点,就一直遍历到NULL节点停止}
}
int main()
{struct Test t1 = {1,NULL};struct Test t2 = {2,NULL};struct Test t3 = {3,NULL};struct Test t4 = {4,NULL};struct Test *head = NULL;struct Test new = {9,NULL};//要改成的节点为9t1.next = &t2;t2.next = &t3;t3.next = &t4;head = &t1;printLink(head);changeLink(head,&new,3);//修改节点3为节点9printLink(head);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 2 9 4  //将节点3改为节点9,如果修改头节点也没有问题 

七,动态创建链表

① 头插法创建链表

每一次创建的新节点插入在上一个头节点之前,再让新创建的节点为链头

#include<stdio.h>
#include<stdlib.h>struct Test//声明结构体类型
{int data;//定义数据域struct Test *next;//定义指针域
};//遍历链表
void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止printf("%d ",p->data);//输出p节点中data的值p = p->next;//使p指向下一节点}printf("\n");
}struct Test* insertFromHead(struct Test* head,struct Test* new)
{if(head == NULL){//判断链表是否为空head = new;//如果为空把创建的新节点当作链表头}else{new->next = head;//当链表不为空的时候,让新节点插在链表头的前面(称之为头插法)head = new;//再让新节点成为链表头}return head;
}//动态创建链表(头插法)
struct Test* creatLink(struct Test* head)
{struct Test* new;while(1){new = (struct Test*)malloc(sizeof(struct Test));//不断为新节点开辟空间new->next = NULL;printf("input your ne node data:\n");scanf("%d",&(new->data));//输入节点的数据域(data)if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建并退出printf("quit!\n");return head;}head = insertFromHead(head,new);}return head;
}int main()
{struct Test* head = NULL;head = creatLink(head);printLink(head);return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
input your ne node data:
1
input your ne node data:
2
input your ne node data:
3
input your ne node data:
4
input your ne node data:
5
input your ne node data:
6
input your ne node data:
0     //输入0就退出程序
quit!
6 5 4 3 2 1  //从头插入,每一个新插入的节点都为链表头

② 尾插法创建链表

如果头节点为空,那么创建的第一个节点就当做链表头,后面每次创建的新节点都依次插在链表最后一个节点的后面

#include<stdio.h>
#include<stdlib.h>struct Test
{int data;//定义数据域struct Test *next;//定义指针域
};//遍历链表
void printLink(struct Test *head)
{struct Test *p = head;while(p != NULL){//p现在是链表头,会一直遍历链表尾NULL时停止printf("%d ",p->data);//输出p节点中data的值p = p->next;//使p指向下一节点}printf("\n");
}struct Test* insertBehind(struct Test* head,struct Test* new)
{struct Test* p = head;if(p == NULL){//判断链表是否为空return head = new;//如果为空把创建的新节点当作链表头}while(p->next != NULL){//遍历到最后一个节点p = p->next;//使p指向下一节点}p->next = new;//把新节点插入最后一个节点的指针域(next)中return head;//把链表头传回去
}//动态创建链表(尾插法)
struct Test* creatLink(struct Test* head)
{struct Test* new;while(1){new = (struct Test*)malloc(sizeof(struct Test));//开辟空间new->next = NULL;printf("input your ne node data:\n");scanf("%d",&(new->data));//输入节点的数据域(data)if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建并退出printf("quit!\n");return head;//返回到头}head = insertBehind(head,new);}
}int main()
{struct Test* head = NULL;head = creatLink(head);printLink(head);}
dhw@dhw-virtual-machine:~$ ./a.out
input your ne node data:
1
input your ne node data:
2
input your ne node data:
3
input your ne node data:
4
input your ne node data:
5
input your ne node data:
6
input your ne node data:
0      //输入0就退出程序
quit!
1 2 3 4 5 6  //按创建节点的顺序,新节点依次往后排列

http://www.ppmy.cn/news/31657.html

相关文章

超过10000人学习的Fiddler抓包教程,只需一小时就可以精通!

如果还是有朋友不太明白的话&#xff0c;可以看看这套视频&#xff0c;有实战讲解 零基础玩转Fiddler抓包在测试领域应用实战&#xff01;一、Fiddler与其他抓包工具的区别 1、Firebug虽然可以抓包&#xff0c;但是对于分析http请求的详细信息&#xff0c;不够强大。模拟http请…

2017系统分析师案例分析真题背记内容

前言 以下内容仅为个人根据当年系分案例真题问题整理的偏需要记背的考点答案&#xff0c;方便个人背诵和记忆使用。方便文字转语音&#xff0c;所以内容全为纯文字内容&#xff0c;以下内容仅供参考。 背记内容 微服务 微服务中应该包含的内容有&#xff1a;资源、对资源的…

Python unittest 模块

一、Unittest 的几个基本概念 TestCase &#xff1a;要写的具体的测试用例TestSuite&#xff1a; 多个测试用例集合&#xff08;或测试套件/测试集&#xff09;TestLoader&#xff1a;用来加载 TestCase 到 TestSuite中的&#xff08;更通俗一点&#xff0c;就是用来把符合我们…

Linux进程学习【进程地址】

✨个人主页&#xff1a; Yohifo &#x1f389;所属专栏&#xff1a; Linux学习之旅 &#x1f38a;每篇一句&#xff1a; 图片来源 &#x1f383;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…

COLMAP

简介&#xff1a;在使用instant-ngp过程中需要使用COLMAP得到模型的必要输入&#xff0c;比如模型需要的相机外参我们就可以通过COLMAP中的sparse reconstruction稀疏重建得到&#xff1b;而对于depth map深度图我们则需要dense reconstruction稠密重建得到&#xff0c;下面我们…

2023-3-8 刷题情况

礼盒的最大甜蜜度 题目描述 给你一个正整数数组 price &#xff0c;其中 price[i] 表示第 i 类糖果的价格&#xff0c;另给你一个正整数 k 。 商店组合 k 类 不同 糖果打包成礼盒出售。礼盒的 甜蜜度 是礼盒中任意两种糖果 价格 绝对差的最小值。 返回礼盒的 最大 甜蜜度。…

【改机教程】iOS系统去除小黑条,改拍照声、拨号音、键盘音,不用越狱,支持所有机型

大家好&#xff0c;上次给大家分享了几个iOS系统免越狱改机教程 今天带来最新的教程&#xff0c;这次修改利用的是同一个漏洞&#xff0c;由外网大神 tamago 开发&#xff0c;国内大神冷风 进行汉化和优化 可以修改的地方包括 去除底部小黑条 dock栏透明 桌面文件夹透明 桌面…

在空投之后,Blur能否颠覆OpenSea的主导地位?

Mar. 2023, Daniel数据源&#xff1a; NFT Aggregators Overview & Aggregator Statistics Overview & Blur Airdrop一年前&#xff0c;通过聚合器进行的NFT交易量开始像滚雪球一样增长&#xff0c;有时甚至超过了直接通过市场平台的交易量。虽然聚合器的使用量从10月到…