你说C语言链表很难?不是吧!快点进来我告诉你。

前言

  很多同学说链表难,总是不懂,看看以下分析就知道了。
  预备知识:
  1.流程控制
  2.数组
  3.函数
  4.指针
  5.字符串
  6.结构体
  7.Linux简单命令使用
  你只有掌握了以上知识点才能更好的掌握C语言链表。以下提供一些我关于指针,字符串,结构体,Linux简单命令使用的知识总结博文链接,这些章节看不懂的同学请点进去。(注意:需要掌握了指针之前的内容才能看懂)
  1.指针请点击
  2.字符串请点击
  3.结构体请点击
  4.Linux请点击

1.什么是链表

1.官方定义

  C语言链表(C linked list)是一种数据结构,用于在内存中组织和存储数据。它由一系列称为节点(node)的元素组成,每个节点包含数据和一个指向下
一个节点的指针。链表的优势在于可以动态地分配和释放内存空间,并且可
以有效地插入和删除节点。

2.自己理解

  1.数组:特点:元素内存地址连续,但对于添加一个元素和删除一个元素不灵活。
  2.链表:链表是数据结构——数据存放的思想。特点:链表的每一项都是一个结构体,结构体内可以是指针,指针可以把整个链表串起来。而且链表很灵活。

2.链表的类型

1.单向链表

  单向链表(Singly Linked List)中的每个节点只有一个指针,指向下一个节点。最后一个节点的指针指向NULL,表示链表的结束。

2.双向链表

  双向链表(Doubly Linked List)中的每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。这样可以实现双向遍历链表。

3.链表的常规操作

  1.创建链表:分配内存空间来存储节点,并设置指针的关系。
  2.插入节点:在链表的特定位置或开头插入节点,需要调整节点间的指针。
  3.删除节点:从链表中删除一个节点,同样需要调整节点间的指针。
  4.遍历链表:按顺序访问链表中的每个节点,可以输出节点的数据或进行其他操作。

4.链表和数组区别及实现

1.链表和数组的区别

  C语言中,链表(linked list)和数组(array)是两种常见的数据结构,它们有以下几点区别:

  1.存储方式:数组是一块连续的内存空间,可以通过索引直接访问任意位置的元素;链表由一系列节点组成,每个节点包含元素和指向下一个节点的指针。

  2.动态性:数组的大小在创建时就确定了,并且不能动态地改变大小;链表的大小可以在运行时动态地增加或减少。

  3.插入和删除操作:对于数组,插入和删除元素可能需要整体移动后续元素的位置,因为数组中的元素是连续存储的;而链表中插入或删除一个节点只需要修改相邻节点的指针,不需要移动其他节点。

  4.内存分配:数组在创建时需要一次性地分配一块连续的内存空间;链表的节点可以分散地存储在不同的内存位置,每个节点可以独立地动态分配。

  5.访问效率:数组通过索引可以快速地访问任意位置的元素,时间复杂度为O(1);链表需要遍历链表,时间复杂度为O(n),其中n为链表长度。然而,链表在插入和删除操作上更高效。

2.数组遍历

  以下是数组遍历代码

#include <stdio.h>

int main()
{
	int i;
	int arr[] = {1,2,3};
	for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
	{
		printf("%d ",arr[i]);
	}
	putchar('\n');
 
	return 0;
}

3.用链表输出1 2 3

  1.核心思想:通过在结构体内定义一个结构体指针变量。然后定义三个结构体变量,分别把变量1的指针变量指向变量2,变量2的指针变量指向变量三,实现把数据串起来。形成链表。
  2.程序代码

#include <stdio.h>
/*
   时间:2023年7月23日20:51:05
   程序功能:链表和数组区别及实现
*/
struct Test            //定义一个结构体Test
{
	int idata;         //定义一个整型idata数据
	struct Test *next; //定义一个结构体Test指针变量next
};

void introduce()
{
	puts("\n\n程序功能:链表和数组区别及实现\n");
	puts("struct Test            //定义一个结构体Test");
	puts("{");
	puts("	int idata;         //定义一个整型idata数据");
	puts("	struct Test *next; //定义一个结构体Test指针变量next");
	puts("};\n\n");
	puts("struct Test t1 ={1,NULL};  //定义一个结构体变量并为其赋值。");
	puts("struct Test t2 ={2,NULL};  //定义一个结构体变量t1,赋值为第一个元素为2,第二个");
	puts("struct Test t3 ={3,NULL};  //指针元素指向空。");
	puts("t1.next = &t2;             //把结构体变量t1中的指针元素next指向第二个结构变量t2");
	puts("t2.next = &t3;             //把结构体变量t2中的指针元素next指向第三个结构变量t3");
	puts("//这样就实现了多个结构体变量串起来。也就形成了链表\n\n");
	puts("printf(“%d %d %d杠n”,t1.idata,t1.next->idata,t1.next->next->idata);");
	puts("//点号的优先级大于箭号");
	puts("");
	puts("");
}

int main()
{
	int i;
	int arr[] = {1,2,3};
	introduce();
	for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
	{
		printf("%d ",arr[i]);
	}
	putchar('\n');
	
	struct Test t1 ={1,NULL};  //定义一个结构体变量并为其赋值。
	struct Test t2 ={2,NULL};  //定义一个结构体变量t1,赋值为第一个元素为2,第二个
	struct Test t3 ={3,NULL};  //指针元素指向空。
	
	t1.next = &t2;             //把结构体变量t1中的指针元素next指向第二个结构变量t2
	t2.next = &t3;             //把结构体变量t2中的指针元素next指向第三个结构变量t3
	                           //这样就实现了多个结构体变量串起来。也就形成了链表。

	printf("以下是用结构体输出数字\n");
	printf("%d %d %d\n",t1.idata,t1.next->idata,t1.next->next->idata);//点号的优先级大于箭号
	
	
	return 0;
}

5.链表静态添加和动态遍历

1.遍历链表函数

void printLlST(struct Test *head)  //定义一个遍历链表的函数
{
	struct Test *point;
	point = head;
	while(point != NULL)           //判断链表结束标志(NULL)
	{
		printf("%d ",point->idata);
		point = point->next;       //移动到下一个元素
	}
}

2.核心思想

  1.在主函数中多定义一个结构体变量t4并初识化为4,NULL
  struct Test t4 = {4,NULL};
  2.把原本的结构体变量T3内的指针变量指向T4
  t3.next = &t4;
  3.最后利用链表遍历函数输出

3.程序代码

#include <stdio.h>
/*
   时间:    2023年7月24日17:19:31
   程序功能:链表静态添加和动态遍历
*/
struct Test            //定义一个结构体Test
{
	int idata;         //定义一个整型idata数据
	struct Test *next; //定义一个结构体Test指针变量next
};

void printLlST(struct Test *head)  //定义一个遍历链表的函数
{
	struct Test *point;
	point = head;
	while(point != NULL)           //判断链表结束标志(NULL)
	{
		printf("%d ",point->idata);
		point = point->next;       //移动到下一个元素
	}
}

void introduce()
{
	puts("\n\n程序功能:链表静态添加和动态遍历\n");
	puts("void printLlST(struct Test *head)  //定义一个遍历链表的函数\n");
	puts("while(point != NULL)           //判断链表结束标志(NULL)\n");
	puts("point = point->next;       //移动到下一个元素\n");
}

int main()
{
	int i;
	int arr[] = {1,2,3};
	introduce();
	for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
	{
		printf("%d ",arr[i]);
	}
	putchar('\n');
	
	struct Test t1 = {1,NULL};  //定义一个结构体变量并为其赋值。
	struct Test t2 = {2,NULL};  //定义一个结构体变量t1,赋值为第一个元素为2,第二个
	struct Test t3 = {3,NULL};  //指针元素指向空。
	struct Test t4 = {4,NULL};
	
	t1.next = &t2;             //把结构体变量t1中的指针元素next指向第二个结构变量t2
	t2.next = &t3;             //把结构体变量t2中的指针元素next指向第三个结构变量t3
	t3.next = &t4;             //这样就实现了多个结构体变量串起来。也就形成了链表。
	

	printf("\n以下是用结构体输出数字\n");
	
	printLlST(&t1);
	
	return 0;
}

4.重点知识研读:point = point->next

  请仔细看以下图片内容
链表遍历函数精析

6.统计链表节点个数及链表查找

1.输出链表节点个数函数

  1.函数代码

int getLinkTotalNodeNum(struct Test *head)  //获取链表节点函数
{
	int cnt = 0;       //定义一个变量cnt用于计数,并初始化为0。
	while(head != NULL)
	{
		cnt++;
		head = head->next; //通过指针移动到下一节点
	}
	return cnt;        //返回计数值。
}

  2.函数核心思想
  函数为整型,形参为结构体指针变量,因此传参为地址。进入函数传参的是t1的地址。判断t1的地址是否为空,不为空cnt自增1为空直接返回 0 。显然第一次不为空,cn自增1,head存放的地址从t1变为t2。这样一直下去,直到head存放的地址为空时,程序退出返回cnt最后的值即节点个数。

2.查找链表节点函数

  1.函数代码

int seekLink(struct Test *head,int data)    //查找链表节点函数
{
	int cnt = 0;               //定义一个变量cnt用于判断是否查找到的条件
	while(head != NULL)
	{
		if(head->idata == data)//对比结构体指针变量head所承接的地址中的idata是否等于需要查找的
		{
			return 1;          //是需要查找的返回1
		}
		head = head->next;     //通过指针移动到下一节点
	}
	return cnt;                //不是需要查找的返回 0 
}

  2.函数核心思想
  核心思想函数为整型,形参为结构体指针变量head和一个整型变量data。进入函数第一句为用于返回0好进行判断。进入while循环判断条件为head存放的地址是否为空不为空执行循环体,为空返回零。进入函数head存放的地址为t1的,不为空。进入if判断,如果此时的data的值等于head中的idata的值,则结束函数并返回1,不等于执行下一语句,下一语句为改变head中的地址。一直这样下去,就可以实现查找节点的功能。

3.完整程序代码

#include <stdio.h>
/*
   时间:    2023年7月24日20:44:30
   程序功能:统计链表节点个数及链表查找
*/
struct Test            //定义一个结构体Test
{
	int idata;         //定义一个整型idata数据
	struct Test *next; //定义一个结构体Test指针变量next
};

void printLlST(struct Test *head)  //定义一个遍历链表的函数
{
	struct Test *point;
	point = head;
	while(point != NULL)           //判断链表结束标志(NULL)
	{
		printf("%d ",point->idata);
		point = point->next;       //通过指针移动到下一节点
	}
	puts("\n");
}

int getLinkTotalNodeNum(struct Test *head)  //获取链表节点函数
{
	int cnt = 0;       //定义一个变量cnt用于计数,并初始化为0。
	while(head != NULL)
	{
		cnt++;
		head = head->next; //通过指针移动到下一节点
	}
	return cnt;        //返回计数值。
}

int seekLink(struct Test *head,int data)    //查找链表节点函数
{
	int cnt = 0;               //定义一个变量cnt用于判断是否查找到的条件
	while(head != NULL)
	{
		if(head->idata == data)//对比结构体指针变量head所承接的地址中的idata是否等于需要查找的
		{
			return 1;          //是需要查找的返回1
		}
		head = head->next;     //通过指针移动到下一节点
	}
	return cnt;                //不是需要查找的返回 0 
}

void introduce()
{
	puts("\n\n程序功能:统计链表节点个数及链表查找\n");
}

int main()
{
	int i;
	int arr[] = {1,2,3};
	int ret;              //用于存放返回的节点数
	int seekNum;          //用于存放需要查找的节点数
	int seekLinkResult;   //用于存放查找出的节点的结果
	
	introduce();
	for(i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
	{
		printf("%d ",arr[i]);
	}
	putchar('\n');
	
	struct Test t1 = {1,NULL};  //定义一个结构体变量并为其赋值。
	struct Test t2 = {2,NULL};  //定义一个结构体变量t1,赋值为第一个元素为2,第二个
	struct Test t3 = {3,NULL};  //指针元素指向空。
	struct Test t4 = {4,NULL};
	
	t1.next = &t2;             //把结构体变量t1中的指针元素next指向第二个结构变量t2
	t2.next = &t3;             //把结构体变量t2中的指针元素next指向第三个结构变量t3
	t3.next = &t4;             //这样就实现了多个结构体变量串起来。也就形成了链表。
	

	printf("\n以下是用结构体输出数字\n");
	printLlST(&t1);
	
	ret = getLinkTotalNodeNum(&t1);
	printf("链表的节点个数为 total num = %d\n",ret);
	
	puts("请输入你要查找的节点");
	scanf("%d",&seekNum);
	seekLinkResult = seekLink(&t1,seekNum);
	if(seekLinkResult == 1)
	{
		printf("在链表中存在你查找的节点 %d\n",seekNum);
	}
	else
	{
		printf("在链表中不存在你查找的链表节点 %d\n",seekNum);
	}
	
	return 0;
}

7.链表从指定节点后方插入新节点

1.查找并插入节点函数

  1.函数代码

void sNodeInsNewN(struct Test *head,int data,struct Test *new)//查找并插入节点函数
{
	struct Test *p =head;
 
	while(p != NULL)
	{
		if(p->idata == data)
		{
			new->next = p->next;
			p->next   = new;
		}
		p = p->next;
	}
}

  2.函数核心思想1
  函数类型为空,形参分别为结构体指针变量head,整型变量data,结构体指针变量new进入函数,首先定义一个结构体指针变量p用于承接head的地址,head中的地址为t1的地址,判断p的地址相当于判断t1的地址是否为空。不为空,进入if语句。判断p中的idata是否等于data,如果data的值等于1,则成立。因为t1中的data的值为1。进入if语句执行体内,执行插入节点功能,先把p中的next赋给new中的next。然后把new的地址赋给p中的next。实现插入节点。如果idata != data,则不会执行插入节点操作。最后执行通过指针实现移动节点。
  3.函数核心思想2:研读new->next = p->next; p->next = new;

  请仔细看以下图片内容
链表插入节点精析

2.完整程序代码

#include <stdio.h>
/*
   时间:    2023年7月24日23:45:33
   程序功能:链表从指定节点后方插入新节点
*/

struct Test
{
	int idata;
	struct Test *next;
};

void printList(struct Test *head) //输出链表函数 
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	putchar('\n');
}

void sNodeInsNewN(struct Test *head,int data,struct Test *new)//查找并插入节点函数
{
	struct Test *p =head;
	
	while(p != NULL)
	{
		if(p->idata == data)
		{
			new->next = p->next;
			p->next   = new;
		}
		p = p->next;
	}
}

int main()
{
	int seekNum;
	char yesOrNoC;
	struct Test t1 = {1,NULL};
	struct Test t2 = {2,NULL};
	struct Test t3 = {3,NULL};
	struct Test t4 = {4,NULL};
	struct Test t5 = {5,NULL};
	
	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	t4.next = &t5;
	
	puts("\n程序功能:链表从指定节点后方插入新节点\n");
	
	do
	{
		puts("\n请输入你要在哪个节点后插入\n");
		scanf("%d",&seekNum);
		struct Test new;
		puts("\n请输入你要插入的数据\n");
		scanf("%d",&new.idata);
		new.next = NULL;
		
		puts("\n初始链表输出结果如下:\n");
		printList(&t1);
		
		puts("\n插入新节点后链表为\n");
		if(seekNum>=1 && seekNum<=5)
		{
			sNodeInsNewN(&t1,seekNum,&new);
			printList(&t1);
			
			//重新初识化链表,不然循环到后面会出问题,链表断开。
			t1.next = &t2;  
	        t2.next = &t3;
	        t3.next = &t4;
	        t4.next = &t5;
			t5.next = NULL;
		}
		else
		{
			puts("插入节点失败,因为指定位置超出1~5");
		}
		
		puts("\n是否继续插入新节点 y/n \n");
		getchar();
		scanf("%c",&yesOrNoC);
		
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("程序结束!!!");
	
	return 0;
}

8.链表从指定节点前方插入新节点

1.查找并插入节点函数

  1.函数代码

struct Test* sNodeInsNewN(struct Test *head,int data,struct Test *new)
{
	struct Test *p = head;
	if(p->idata == data)
	{
		new->next = head;
		puts("插入节点成功\n");
		return new;
	}
	while(p->next != NULL)
	{
		if(p->next->idata == data)
		{
			new->next = p->next;
			p->next = new;
			puts("插入节点成功\n");
			return head;
		}
		p = p->next;
	}
	puts("插入节点失败!因为指定位置超出范围,原样输出。\n");
	return head;
 
}

  2.函数核心思想

  请仔细看以下图片内容
请添加图片描述

2.完整程序代码

#include <stdio.h>
/*
   时间:    2023年7月26日01:07:03
   程序功能:链表从指定节点前方插入新节点
*/

struct Test
{
	int idata;
	struct Test *next;
};

void printList(struct Test *head) 
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	puts("\n");
}

struct Test* sNodeInsNewN(struct Test *head,int data,struct Test *new)
{
	struct Test *p = head;
	if(p->idata == data)
	{
		new->next = head;
		puts("插入节点成功\n");
		return new;
	}
	while(p->next != NULL)
	{
		if(p->next->idata == data)
		{
			new->next = p->next;
			p->next = new;
			puts("插入节点成功\n");
			return head;
		}
		p = p->next;
	}
	puts("插入节点失败!因为指定位置超出范围,原样输出。\n");
	return head;
	
}

int main()
{
	int seekNum;
	char yesOrNoC;
	struct Test t1 = {1,NULL};
	struct Test t2 = {2,NULL};
	struct Test t3 = {3,NULL};
	struct Test t4 = {4,NULL};
	struct Test t5 = {5,NULL};
	struct Test new;
	struct Test *head;
	
	
	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	t4.next = &t5;
	head = &t1;
	
	puts("\n程序功能:链表从指定节点前方插入新节点\n");
	
	puts("\n初始化链表输出如下。\n");
	printList(&t1);
	
	do
	{
		puts("\n请输入你需要插入的节点位置\n");
		scanf("%d",&seekNum);
		puts("\n请输入你要插入的数据\n");
		scanf("%d",&new.idata);
		putchar('\n');
		new.next = NULL;
		head = sNodeInsNewN(&t1,seekNum,&new);
		printList(head);
		
		t1.next = &t2;
		t2.next = &t3;
		t3.next = &t4;
		t4.next = &t5;
		head    = &t1;
		
		puts("你是否继续插入节点?y/n\n");
		getchar();
		scanf("%c",&yesOrNoC);
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("\n程序结束!!!");
	
	return 0;
}

9.链表删除指定节点

1.查找并删除节点函数

  1.函数代码

struct Test *deleteNode(struct Test *head,int data)
{
	struct Test *p = head;
	if(p->idata == data)  //注意逻辑等于 ==
	{
		puts("\n删除节点成功输出\n");
		head = head->next;
		return head;
	}
 
	while(p->next != NULL)
	{
		if(p->next->idata == data)
		{
			p->next = p->next->next;
			puts("\n删除节点成功输出\n");
			return head;
		}
		p = p->next;
	}
 
	puts("删除节点失败,输入节点超出范围!原样输出\n");
	return head;
}

  2.函数核心思想

  请仔细看以下图片内容
请添加图片描述

2.完整程序代码

#include <stdio.h>
/*
   时间:    2023年7月26日22:51:27
   程序功能:链表删除指点节点
*/

struct Test
{
	int idata;
	struct Test *next;
};

void printList(struct Test *head)
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	puts("\n");
}

struct Test *deleteNode(struct Test *head,int data)
{
	struct Test *p = head;
	if(p->idata == data)  //注意逻辑等于 ==
	{
		puts("\n删除节点成功输出\n");
		head = head->next;
		return head;
	}
	
	while(p->next != NULL)
	{
		if(p->next->idata == data)
		{
			p->next = p->next->next;
			puts("\n删除节点成功输出\n");
			return head;
		}
		p = p->next;
	}
	
	puts("删除节点失败,输入节点超出范围!原样输出\n");
	return head;
}

int main()
{
	int seekNum;
	char yesOrNoC;
	struct Test t1 = {1,NULL};
	struct Test t2 = {2,NULL};
	struct Test t3 = {3,NULL};
	struct Test t4 = {4,NULL};
	struct Test t5 = {5,NULL};
	struct Test *head;
	
	t1.next = &t2;
	t2.next = &t3;
	t3.next = &t4;
	t4.next = &t5;
	head = &t1;
	
	puts("\n程序功能:链表删除指点节点\n");
	
	puts("\n初始化链表输出\n");
	printList(&t1);
	do
	{
		puts("\n请输入你要删除的节点数\n");
		scanf("%d",&seekNum);
		head = deleteNode(head,seekNum);
		printList(head);
		
		//重置链表
		t1.next = &t2;
		t2.next = &t3;
		t3.next = &t4;
		t4.next = &t5;
		head    = &t1;
		
		puts("是否继续删除链表节点?y/n\n");
		getchar();
		scanf("%c",&yesOrNoC);
		
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("\n程序结束!!!\n");
	
	return 0;
}

10.链表动态创建之头插法

1.动态创建链表头插法函数

  1.函数代码

struct Test *InsertFormHead(struct Test *head)  //动态创建链表头插法函数
{
	struct Test *new;
 
	while(1)
	{
		new = (struct Test *)malloc(sizeof(struct Test));
		puts("\n请输入你要插入的节点数据\n");
		scanf("%d",&(new->idata));
		new->next = NULL;
		if(new->idata == 0)
		{
			puts("\n你输入的数据为0,不符合!程序退出。\n");
			return head;
		}
		if(head == NULL)
		{
			head = new;
		}
		else
		{
			new->next = head;
			head = new;
		}
	}
}

  2.函数核心思想

  请仔细看以下图片内容
请添加图片描述

2.整个程序代码

#include <stdio.h>
#include <stdlib.h>
/*
   时间:    2023年7月28日01:04:56
   程序功能:链表动态创建之头插法(基础版)
*/

struct Test 
{
	int idata;
	struct Test *next;
};

void printList(struct Test *head)
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	puts("\n");
}

struct Test *InsertFormHead(struct Test *head)  //动态创建链表头插法函数
{
	struct Test *new;
	
	while(1)
	{
		new = (struct Test *)malloc(sizeof(struct Test));
		puts("\n请输入你要插入的节点数据\n");
		scanf("%d",&(new->idata));
		new->next = NULL;            //不为new中的结构体指针变量next赋空,会出现输出时死循环,
		if(new->idata == 0)          //因为没有赋值时next为野指针。历循地址时会出错。
		{
			puts("\n你输入的数据为0,不符合!程序退出。\n");
			return head;
		}
		if(head == NULL)
		{
			head = new;
		}
		else
		{
			new->next = head;
			head = new;
		}
	}
}

struct Test *freeList(struct Test *head)//释放动态链表内存函数
{
	struct Test *p = head;
	while(p != NULL)
	{
		p = p->next;   //先让p历循到下一节点
		free(head);    //再把链表头内存释放掉
		head = p;      //最后把下一节p点赋给head
		               //依次循环下去即可把动态创建的链表内存空间释放。
	}
	return head;
}

int main()
{
	struct Test * head = NULL;
	char yesOrNoC;
	
	puts("\n程序功能:链表动态创建之头插法(基础版)\n");
	
	do
	{
		head = InsertFormHead(head);
		printList(head);
		head = freeList(head);
		puts("你是否继续动态创建链表?y/n\n");
		getchar();
		scanf("%c",&yesOrNoC);
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("\n程序退出!!!");
	
	return 0;
}

3.整个程序代码改进版——改进就只是把头插法和动态创建链表分别封装成一个函数

#include <stdio.h>
#include <stdlib.h>
/*
   时间:    2023年7月28日01:04:56
   程序功能:链表动态创建之头插法(优化版)
*/

struct Test 
{
	int idata;
	struct Test *next;
};

void printList(struct Test *head)
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	puts("\n");
}

struct Test *InsertFormHead(struct Test *head,struct Test *new)
{
		if(head == NULL)
		{
			head = new;
		}
		else
		{
			new->next = head;
			head = new;
		}
		return head;
	
}

struct Test *createList(struct Test *head)
{
	struct Test *new;
	while(1)
	{
		new = (struct Test *)malloc(sizeof(struct Test));
		puts("\n请输入你要插入的节点数据\n");
		scanf("%d",&(new->idata));
		new->next = NULL;
		if(new->idata == 0)
		{
			puts("\n你输入的数据为0,不符合!程序退出。\n");
			free(new);
			return head;
		}
		head = InsertFormHead(head,new);
	}
	
}

struct Test *freeList(struct Test *head)//释放动态链表内存函数
{
	struct Test *p = head;
	while(p != NULL)
	{
		p = p->next;   //先让p历循到下一节点
		free(head);    //再把链表头内存释放掉
		head = p;      //最后把下一节p点赋给head
		               //依次循环下去即可把动态创建的链表内存空间释放。
	}
	return head;
}

int main()
{
	struct Test * head = NULL;
	char yesOrNoC;
	
	puts("\n程序功能:链表动态创建之头插法(基础版)\n");
	
	do
	{
		head = createList(head);
		printList(head);
		head = freeList(head);
		puts("你是否继续动态创建链表?y/n\n");
		getchar();
		scanf("%c",&yesOrNoC);
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("\n程序退出!!!");
	
	return 0;
}

11.链表动态创建之尾插法

1.动态创建链表尾插法函数

  1.函数代码

struct Test *InsertFormTail(struct Test *head,struct Test *new)  //动态创建链表尾插法函数
{
	struct Test *p = head;
	if(p == NULL)
	{
		head = new;
		return head;
	}
	while(p->next != NULL)
	{
		p = p->next;
	}
	p->next = new;
 
	return head;
}

  2.函数核心思想

  请仔细看以下图片内容
请添加图片描述

2.释放动态创建链表的函数

  1.函数代码

struct Test *freeList(struct Test *head)
{
	struct Test *tmp = head;
	while(tmp != NULL)
	{
		tmp = tmp->next;
		free(head);
		head = tmp;
	}
 
	return head;
}

  2.函数核心思想

  请仔细看以下图片内容
请添加图片描述

3.完整程序代码

#include <stdio.h>
#include <stdlib.h>
/*
   时间:    2023年7月29日00:51:12
   程序功能:链表动态创建之尾插法
*/

struct Test
{
	int idata;
	struct Test *next;
};

void *printList(struct Test *head)//输出链表函数。
{
	struct Test *p = head;
	while(p != NULL)
	{
		printf("%d ",p->idata);
		p = p->next;
	}
	puts("\n");
}

struct Test *InsertFormTail(struct Test *head,struct Test *new)  
{   //动态创建链表尾插法函数
	struct Test *p = head;
	if(p == NULL)
	{
		head = new;
		return head;
	}
	while(p->next != NULL)
	{
		p = p->next;
	}
	p->next = new;
	
	return head;
}

struct Test *createList(struct Test *head)//动态创建链表函数
{
	struct Test *new;
	while(1)
	{
		new = (struct Test *)malloc(sizeof(struct Test));
		puts("\n请输入你要插入的节点数据\n");
		scanf("%d",&(new->idata));
		new->next = NULL;
		if(new->idata == 0)
		{
			puts("\n输入的节点数据为零,不符合,输入结束!\n");
			free(new);
			return head;
		}
		head = InsertFormTail(head,new);
	}
}

struct Test *freeList(struct Test *head)//释放动态创建链表内存函数。
{
	struct Test *tmp = head;
	while(tmp != NULL)
	{
		tmp = tmp->next;
		free(head);
		head = tmp;
	}
	
	return head;
}

int main()
{
	struct Test *head = NULL;
	char yesOrNoC;
	
	puts("\n程序功能:链表动态创建之尾插法\n");
	
	do
	{
		head = createList(head);
		printList(head);
		head = freeList(head);
		puts("你是否继续用尾插法创建动态链表?y/n\n");
		getchar();
		scanf("%c",&yesOrNoC);
		
	}while(yesOrNoC=='y' || yesOrNoC=='Y');
	
	puts("\n程序结束!!!");
	
	return 0;
}

结束语

  您能在百忙之中抽出时间看到这里,我很感谢,也很高兴。链表的难点在于你对链表之前的知识是否掌握足够扎实,链表是一种数据存放的思想,所以只要你之前的知识掌握好,链表还是能够独立完成的。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值