【数据结构-初阶】详解线性表(2)---单链表

🎈主页传送门****:良木生香

🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》

🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离

目录

1、链表

1.1、什么是链表?

1.2、链表的分类

2、单链表的实现

2.1、单链表的头文件

2.2、单链表的结构

2.3、单链表节点的创建

2.4、单链表的初始化

2.5、单链表的插入操作

2.5.1、单链表的头部插入

2.5.2、单链表的尾部插入

2.5.3、单链表的pos位置插入

2.5.3.1、单链表的长度计算

2.5.3.2、查找pos位置上的元素

2.5.3.3、pos位置之前的插入

2.5.3.4、pos位置之后的插入

2.6、单链表的删除操作

2.6.1、单链表的头部删除

2.6.2、单链表的尾部删除

2.6.3、单链表的pos位置删除

2.7、单链表的查找操作

2.8、单链表的修改操作

2.9单链表的打印

2.10、单链表的销毁

3、总体实现:


**上期回顾:**在上一篇文章中(这是链接:【数据结构-初阶】详解线性表(1)---顺序表),我们学习了第一种数据结构---顺序表,这是一种逻辑上连续,物理上也连续的数据存储方式,文章中我们也提到,也有逻辑上连续,但物理上不连续的数据存储方式,那这就是我今天分享的内容了------单链表


前言:在开始之前,我想先与大家探讨探讨什么是链表.都说链表链表,那顾名思义就是像有链子一样把数据串起来的表格吧.那具体是啥呢?又有哪些分类与作用呢?接下来我们一一讲解

1、链表

1.1、什么是链表?

概念链表是一种物理存储结构上非连续、非顺序的存储结构,数据的元素逻辑顺序是通过链表中的指针次序实现的。

单单这样子讲大家可能会有些疑惑,链表到底是什么啊!!!不着急,我就拿火车来给大家举个例子吧。火车是有一节一节车厢的,每节车厢是由车厢之间的挂钩连接在一起的,那么链表的节点就相当于每节车厢,而每个节点里面所存放的指针就相当于车厢之间的挂钩:

**链表中的每个节点都存放着当前节点的数组data,我们将其称之为---****数据域,****以及下一个节点的指针,我们将其称之为---****指针域,**当我们在对链进行操作时,就可以通过当前链表所存放的指针来找到下一个链表节点.

1.2、链表的分类

在链表这个大家族中,有许多种类的链表,有我们今天要讲的单链表,也有后面会学到的双链表,有带头节点的,也有不带头节点的,有循环也有不循环的,下面一张图带你了解链表的分类:

这样的话,链表就被分成了八个类别,而我们今天要讲的就是,单项不带头结点不循环链表.

2、单链表的实现

讲完了链表的概念之后,下面我们就正式进入单链表的学习之中。

2.1、单链表的头文件

在实现链表之前,我们先将所需要的头文件写好:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>        //基本函数的使用
#include<stdlib.h>        //为了使用动态内存管理函数
#include<windows.h>        //为了实现清屏的功能
#include<assert.h>        //用于指针断言
//接下来的代码的是实现单向不带头节点不循环链表

//现在重命名变量
typedef int Elemtype;

实现单链表的功能.我们依旧可以通过增、删、查、改四个方向来操作

2.2、单链表的结构

单链表的操作是对每个车厢节点进行操作的,那现在我们来构建它的车厢节点的基本结构,主要有数据域指针域组成:

像现在来看看我们的结构体代码

cpp 复制代码
typedef struct ListNode {
	Elemtype data;
	ListNode* next;
}ListNode;

这样子写家人们认为对吗?我让这个结构体指针next指向ListNode*类型的,这样写可以吗?

当然不行,为啥?因为C语言是向上编译的,也就是说,在编译的时候,程序会向上寻找ListNode类型的变量,但是它只能找到struct ListNode*,换句话说就是ListNode这个重命名的名字是在结构体生成之后才有的,而在结构体里面没有这个名字,所以不能用重命名的名字对结构体里面的变量命名,正确的代码如下:

cpp 复制代码
typedef struct ListNode {
	Elemtype data;
	struct ListNode* next;
}ListNode;

next指针应该用struct ListNode 类型,经过最后的重命名为ListNode之后,后续再调用next就可以用ListNode* 类型了.

2.3、单链表节点的创建

我们现在已经知道了,链表是由一个一个节点连在一起的,那么我们对链表进行操作,实际上就是对链表的节点进行操作,这里少不了的就是创建,也就是向内存申请空间:

cpp 复制代码
//现在是创建链表节点的函数
ListNode* Buy_Node(Elemtype data) {
	//向内存申请空间
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (newNode == NULL) {
		printf("申请空间失败\n");
		return NULL;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}

在这个函数中,我们传入一个参数data,作为数据域的数据,然后向内存申请大小为ListNode的空间存放链表节点,将data赋值给节点中的data变量,并将要指向下一个节点的指针next置为NULL,因为现在只是创建节点,并不用指向某个节点.

2.4、单链表的初始化

对于单链表的初始化,这里就有讲究了。我们今天要实现的是不带头结点的单项不循环链表,那么就不会有头结点,也就是意味着,头结点(也叫做哨兵位)与链表的第一个节点是重合的,不明白什么意思的话就看看下面这张图:

对于这种没有头节点的,我们初始化的方法就不一样了,我们是对第一个节点的指针进行初始化(本质上是对指针内容进行修改),因为在最开始的时候还没有创建链表节点,所以我们要将指向第一个节点的指针置为NULL,这就算是初始化成功了

但是想要修改一个指针的内容,我们应该怎么呢?当然是闯入这个指针的地址,用二级指针接收,图示如下:

初始化的本质就是将头指针置为NULL!!!!!!

代码如下:

cpp 复制代码
void Init_ListNode(ListNode** pphead) {
	assert(pphead);        //记得对传进来的指针进行断言
	*pphead = NULL;
}

2.5、单链表的插入操作

插入操作与顺序表一样,同样分为头插,尾插 ,pos之前之后插入,下面就来一一实现:

2.5.1、单链表的头部插入

对于头部插入,实现的是使数据先进后出的功能,这会让头指针不断改变指向的节点,想要修改头指针指向的节点,我们就要用二级指针来接收头指针的地址,通过头指针的二级指针改头指针的指向节点:

我们要将头结点不断修改,保证他能一直指向新加入的节点.明白了插入的流程之后,那我们就上代码吧~~~~

cpp 复制代码
void Push_Front(ListNode** pphead, Elemtype data) {
	assert(pphead);    //对指针进行断言
	ListNode* newNode = Buy_Node(data);    //创建新节点
	if (newNode == NULL) {
		printf("创建新节点失败...\n");
		return;
	}
	//newnode   *pphead
	newNode->next = *pphead;    //连接新新节点与头指针
	*pphead = newNode;    //将头指针指向新节点
}

小贴士:记得传入数据data~~~

2.5.2、单链表的尾部插入

尾部插入与头部插入大相庭径,只要找到最后一个节点,将新节点连接到最后一个节点即可.因为要找到尾结点,所以我们要传入头结点,定义一个遍历链表的节点来找到尾结点:

代码如下:

cpp 复制代码
void Push_Back(ListNode** pphead, Elemtype data) {
	assert(pphead);
	ListNode* newNode = Buy_Node(data);
	//判断是否为头节点
	if (*pphead == NULL) {
		*pphead = newNode;
		return;
	}
	//  *pphead   *pphead->next   newNode
	//                  ptail
	else {
		ListNode* ptail = *pphead;
		while (ptail->next != NULL) {
			ptail = ptail->next;
		}
		ptail->next = newNode;
	}
}

小贴士:再插入之前,我们要先判断是否存在头结点,如果不存在头结点,我们要将头指针指向该节点

2.5.3、单链表的pos位置插入

2.5.3.1、单链表的长度计算

我们先对链表长度进行计算,后面可能会用到,就算用不到,我们也可以多出一个求长度的功能.求长度的基本思路就是用一个遍历链表的指针,计算节点的指针,代码如下:

cpp 复制代码
int Get_ListLength(ListNode** pphead) {
	assert(pphead);    //依旧记得断言指针
	int len = 0;
	ListNode* pcur = *pphead;
	while (pcur != NULL) {
		len++;
		pcur = pcur->next;
	}
	return len;
}

小贴士:在while()循环条件中,不能写pcur->next!=NULL,因为这样会让指针最终指向的只是倒数第二个节点,不能完全计算链表的长度

2.5.3.2、查找pos位置上的元素

想要对pos位置前后进行操作,就要先判断pos这个位置是否存在元素,如果有就返回pos位置上节点的指针,以便于后续的操作:

cpp 复制代码
ListNode* Search_pos_elem(ListNode** pphead, int pos) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 0||pos>len) {
		printf("pos值不合法...\n");
		return NULL;
	}
	ListNode* ppos = *pphead;
    if(pos == 1){
        return *pphead;
    }
	for (int i = 1; i < pos; i++) {
		if (ppos == NULL) {
			printf("pos位置没有元素...\n");
			return NULL;
		}
		ppos = ppos->next;
	}
	return ppos;
}

小贴士:在查找pos位置上的元素时,要先判断pos值的合法性,在看看pos是不是==1,如果是就返回第一个节点,最后在进行遍历.

2.5.3.3、pos位置之前的插入

我们可以通过一下步骤进行操作:

1.判断pos值

2.if(pos==1),那就头插,else:找出pos位置节点,再找出pos的前一个节点prev_node

3.在prev_node与pos之间进行插入操作

代码如下:

cpp 复制代码
void Push_pos_Front(ListNode** pphead, Elemtype data, int pos) {
	assert(pphead);
	//ListNode* PosNode = Search_elem_for_posFront(pphead, pos);
	ListNode* newNode = Buy_Node(data);
	int len = Get_ListLength(pphead);
	if (pos<1 || pos>len) {
		printf("pos值不合法,该位置无法插入...\n");
		return;
	}
	if (pos == 1) {
		//相当于头插
		Push_Front(pphead, data);
		return;
	}
	else {
		ListNode* pos_node = Search_pos_elem(pphead, pos);
		ListNode* prev = *pphead;
		while (prev->next != pos_node) {
			prev = prev->next;
		}
		//prev_node		newNode		pos
		newNode->next = pos_node;
		prev->next = newNode;
	}
}

小贴士:该函数涉及多个函调用,在操作的时候要小心哦

2.5.3.4、pos位置之后的插入

这个操作会比上一个简单,只需要找到pos位置的节然后进行操作即可,步骤与前一个就少了找到prev_node节点而已,

直接上代码:

cpp 复制代码
void Push_pos_Back(ListNode** pphead, Elemtype data, int pos) {
	assert(pphead);
	ListNode* newNode = Buy_Node(data);
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		return;
	}
	//PosNode   newNode    PosNode->next;
	newNode->next = PosNode->next;
	PosNode->next = newNode;
}

那么以上就是单链表插入操作的所有代码了,有不对的地方希望大佬们指出来~~~

2.6、单链表的删除操作

2.6.1、单链表的头部删除

单链表的头部删除依旧是涉及到头指针的指向问题,那么我们就要传入头指针的指针,操作相对简单,但是要注意的是,要先给头结点赋值上一个删除的指针变量,将现在头结点的指针指向下一个节点,在释放删除的指针变量

代码如下:

cpp 复制代码
void Pop_Front(ListNode** pphead) {
	assert(pphead);
	//*pphead  *pphead->next   *pphead->next->next
	//delet    *pphead
	if (*pphead == NULL) {
		printf("链表为空,无法删除...\n");
		return;
	}
	ListNode* delet = *pphead;
	*pphead = delet->next;
	free(delet);
	delet = NULL;
}

小贴士:释放空间之后记得将delet指针置为空哦,不然会成为野指针的~~~~~

2.6.2、单链表的尾部删除

想要进行单链表的尾删,就少不了找出尾指针,肯定是需要遍历的,具体过程可以看下图,步骤与头删相类似:

小贴士:释放完空间之后记得将delet指针置为NULL~~~

2.6.3、单链表的pos位置删除

因为删除是对单个元素进行操作的,就不用分pos之前和pos之后了,直接删掉就行,要注意的是,删掉pos位置节点之后,要处理好pos前后节点的连接:

下面上代码:

cpp 复制代码
//现在是实现pos位置的删除
void Pop_pos(ListNode** pphead, int pos) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 1||pos>len) {
		printf("pos位置不合法...\n");
		return;
	}
	if (*pphead == NULL) {
		printf("链表为空,无法删除...\n");
		return;
	}
	if (pos == 1) {
		Pop_Front(pphead);
		return;
	}
	//先查找pos位置的元素;
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		printf("该位置没有元素...\n");
		return;
	}
	//ppos    PosNode    PosNode->next
	//ppos      delet     
	ListNode* ppos = *pphead;
	while (ppos->next != PosNode) {
		ppos = ppos->next;
	}
	ListNode* delet = PosNode;
	ppos->next = delet->next;
	free(delet);
	delet = NULL;
	PosNode = NULL;
}

小贴士:在删除的时候我们要判断好头结点与空链表,最后韩信带净化(还是那句话),记得将delet指针值为NULL

2.7、单链表的查找操作

这个查找的操作是用已知元素去检验目标元素是否存在,只需要遍历一次链表即可:

cpp 复制代码
//现在是查找目标元素函数
void Search_elem(ListNode** pphead, Elemtype data) {
	assert(pphead);
	ListNode* pcur = *pphead;
	int count = 0;
	int flag = 0;
	while (pcur != NULL) {
		count++;
		if (pcur->data == data) {
			printf("找到了!在第%d个位置\n", count);
			flag = 1;
			break;
		}
		pcur = pcur->next;
	}
	if (flag == 0) {
		printf("没找到你想要的元素...\n");

	}
}

小贴士:我们要注意,有两种情况会退出循环:

1.找到元素了.退出循环;

2.遍历整个数组,都没有找到,退出循环.

我们自然要对于这两种情况进行区分

2.8、单链表的修改操作

这个操作是整个操作系列中最简单的了,只用找到pos位置元素,直接修改即可:

cpp 复制代码
//现在实现修改功能
void Change_elem_pos(ListNode** pphead, int pos, Elemtype data) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 1 || pos>len) {
		printf("pos值不合法...\n");
		return;
	}
	//依旧先查找
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		printf("该位置没有元素,无法修改...\n");
		return;
	}
	PosNode->data = data;
}

小贴士:要注意判断pos的值是否合法~~~

2.9单链表的打印

这个操作只用遍历,然后纯纯输出:

cpp 复制代码
//现在是打印链表函数
void my_printf(ListNode* phead) {
	//assert(phead);
	if (phead == NULL)
	{
		printf("当前链表为空...\n");
		return;
	}
	ListNode* pcur = phead;
	while (pcur != NULL) {
		printf("%d ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

小贴士:没有小贴士~~~

2.10、单链表的销毁

韩信带净化(还是那句话),遍历链表,逐个节点逐个节点地销毁

cpp 复制代码
//现在是销毁链表函数
void Destory_ListNode(ListNode** pphead) {
	assert(pphead);
	ListNode* pcur = *pphead;
	
	while (pcur != NULL) {
		ListNode* delet = pcur;
		pcur = pcur->next;
		free(delet);
		delet = NULL;
	}
	*pphead = NULL;
    pcur = NULL;
}

小贴士:最后记得将头指针与delet、pcur置为空~~~~

3、总体实现:

既然上面讲了这么多,现在我们就来将代码们整合一下吧~~~有点长,嘻嘻

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<assert.h>
//接下来的代码的是实现单向不带头节点链表


//现在重命名变量
typedef int Elemtype;

//现在定义链表结构
typedef struct ListNode {
	Elemtype data;
	struct ListNode* next;
}ListNode;




//基本的函数声明
void Init_ListNode(ListNode** phead);   //初始化
ListNode* Buy_Node(Elemtype data);       //创建新节点
int Get_ListLength(ListNode** pphead);   //计算链表长度

//插入
void Push_Front(ListNode** pphead, Elemtype data);		//头插
void Push_Back(ListNode** pphead, Elemtype data);		//尾插
//ListNode* Search_elem_for_posFront(ListNode** pphead, int pos);		//查找pos位置的前一个元素
void Push_pos_Front(ListNode** pphead, Elemtype data, int pos);		//pos位置之前插入
ListNode* Search_pos_elem(ListNode** pphead, int pos);		//查找pos位置上的元素
void Push_pos_Back(ListNode** pphead, Elemtype data, int pos);		//pos位置之后插入

//删除
void Pop_Front(ListNode** pphead);		//头删
void Pop_Back(ListNode** pphead);		//尾删
void Pop_pos(ListNode** pphead, int pos);		//pos位置删除

//修改
void Change_elem_pos(ListNode** pphead, int pos, Elemtype data);

//查找
void Search_elem(ListNode** pphead, Elemtype data);

//其他
void my_printf(ListNode* phead);		//打印链表
void Destory_ListNode(ListNode** pphead);		//销毁链表
void printf_menu();		//打印菜单






//现在是初始化链表
void Init_ListNode(ListNode** pphead) {
	assert(pphead);
	*pphead = NULL;
}

//现在是创建链表节点的函数
ListNode* Buy_Node(Elemtype data) {
	//向内存申请空间
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (newNode == NULL) {
		printf("申请空间失败\n");
		return NULL;
	}
	newNode->data = data;
	newNode->next = NULL;
	return newNode;
}



//计算链表长度
int Get_ListLength(ListNode** pphead) {
	assert(pphead);
	int len = 0;
	ListNode* pcur = *pphead;
	while (pcur != NULL) {
		len++;
		pcur = pcur->next;
	}
	return len;
}

//现在实现链表
//插入操作:头部插入:
void Push_Front(ListNode** pphead, Elemtype data) {
	assert(pphead);
	ListNode* newNode = Buy_Node(data);
	if (newNode == NULL) {
		printf("创建新节点失败...\n");
		return;
	}
	//newnode   *pphead
	newNode->next = *pphead;
	*pphead = newNode;
}


//现在是尾部插入
void Push_Back(ListNode** pphead, Elemtype data) {
	assert(pphead);
	ListNode* newNode = Buy_Node(data);
	//判断是否为头节点
	if (*pphead == NULL) {
		*pphead = newNode;
		return;
	}
	//  *pphead   *pphead->next   newNode
	//                  ptail
	else {
		ListNode* ptail = *pphead;
		while (ptail->next != NULL) {
			ptail = ptail->next;
		}
		ptail->next = newNode;
	}
}


//现在是pos位置之前的插入
//现在这个函数是寻找pos位置的元素,看看pos位置之前的一个位置是否有元素
ListNode* Search_elem_for_posFront(ListNode** pphead, int pos) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos<1 || pos>len + 1) {
		printf("pos值不合法,链表长度为:%d", len);
		return NULL;
	}
	if (pos == 1) {
		return NULL;
	}
	ListNode* ppos = *pphead;
	for (int i = 1; i < pos - 1; i++) {
		if (ppos == NULL) {
			printf("pos位置没有元素...\n");
			return NULL;
		}
		ppos = ppos->next;
	}
	if (ppos == NULL) {
		printf("pos位置没有元素...\n");
		return NULL;
	}
	return ppos;

}


//先在再来实现pos位置之前插入
void Push_pos_Front(ListNode** pphead, Elemtype data, int pos) {
	assert(pphead);
	//ListNode* PosNode = Search_elem_for_posFront(pphead, pos);
	ListNode* newNode = Buy_Node(data);
	int len = Get_ListLength(pphead);
	if (pos<1 || pos>len) {
		printf("pos值不合法,该位置无法插入...\n");
		return;
	}
	if (pos == 1) {
		//相当于头插
		Push_Front(pphead, data);
		return;
	}
	else {
		ListNode* pos_node = Search_pos_elem(pphead, pos);
		ListNode* prev = *pphead;
		while (prev->next != pos_node) {
			prev = prev->next;
		}
		//prev_node		newNode		pos
		newNode->next = pos_node;
		prev->next = newNode;
	}
}


//现在实现查找pos位置元素的函数,本质上是为了给pos位置之后插入进行辅助,但是同时也实现了查找的功能
ListNode* Search_pos_elem(ListNode** pphead, int pos) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 0||pos>len) {
		printf("pos值不合法...\n");
		return NULL;
	}
	ListNode* ppos = *pphead;
	if (pos == 1) {
		return *pphead;
	}
	for (int i = 1; i < pos; i++) {
		if (ppos == NULL) {
			printf("pos位置没有元素...\n");
			return NULL;
		}
		ppos = ppos->next;
	}
	return ppos;
}
//现在是实现pos位置之后来实现插入
void Push_pos_Back(ListNode** pphead, Elemtype data, int pos) {
	assert(pphead);
	ListNode* newNode = Buy_Node(data);
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		return;
	}
	//PosNode   newNode    PosNode->next;
	newNode->next = PosNode->next;
	PosNode->next = newNode;
}




//现在是实现删除元素
//头删
void Pop_Front(ListNode** pphead) {
	assert(pphead);
	//*pphead  *pphead->next   *pphead->next->next
	//delet    *pphead
	if (*pphead == NULL) {
		printf("链表为空,无法删除...\n");
		return;
	}
	ListNode* delet = *pphead;
	*pphead = delet->next;
	free(delet);
	delet = NULL;
}


//现在是实现尾部删除的操作;
void Pop_Back(ListNode** pphead) {
	assert(pphead);
	//定义尾指针
	if (*pphead == NULL) {
		printf("链表为空,无法删除...\n");
		return;
	}
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
		return;
	}
	ListNode* ptail = *pphead;
	while (ptail->next->next != NULL) {
		ptail = ptail->next;
	}
	//ptail   ptail->next   NULL
	// ptail     delet
	ListNode* delet = ptail->next;
	ptail->next = NULL;
	free(delet);
	delet = NULL;
}



//现在是实现pos位置的删除
void Pop_pos(ListNode** pphead, int pos) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 1||pos>len) {
		printf("pos位置不合法...\n");
		return;
	}
	if (*pphead == NULL) {
		printf("链表为空,无法删除...\n");
		return;
	}
	if (pos == 1) {
		Pop_Front(pphead);
		return;
	}
	//先查找pos位置的元素;
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		printf("该位置没有元素...\n");
		return;
	}
	//ppos    PosNode    PosNode->next
	//ppos      delet     
	ListNode* ppos = *pphead;
	while (ppos->next != PosNode) {
		ppos = ppos->next;
	}
	ListNode* delet = PosNode;
	ppos->next = delet->next;
	free(delet);
	delet = NULL;
	PosNode = NULL;
}




//现在实现修改功能
void Change_elem_pos(ListNode** pphead, int pos, Elemtype data) {
	assert(pphead);
	int len = Get_ListLength(pphead);
	if (pos < 1 || pos>len) {
		printf("pos值不合法...\n");
		return;
	}
	//依旧先查找
	ListNode* PosNode = Search_pos_elem(pphead, pos);
	if (PosNode == NULL) {
		printf("该位置没有元素,无法修改...\n");
		return;
	}
	PosNode->data = data;
}


//现在是打印链表函数
void my_printf(ListNode* phead) {
	//assert(phead);
	if (phead == NULL)
	{
		printf("当前链表为空...\n");
		return;
	}
	ListNode* pcur = phead;
	while (pcur != NULL) {
		printf("%d ", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}


//现在是查找目标元素函数
void Search_elem(ListNode** pphead, Elemtype data) {
	assert(pphead);
	ListNode* pcur = *pphead;
	int count = 0;
	int flag = 0;
	while (pcur != NULL) {
		count++;
		if (pcur->data == data) {
			printf("找到了!在第%d个位置\n", count);
			flag = 1;
			break;
		}
		pcur = pcur->next;
	}
	if (flag == 0) {
		printf("没找到你想要的元素...\n");

	}
}


//现在是销毁链表函数
void Destory_ListNode(ListNode** pphead) {
	assert(pphead);
	ListNode* pcur = *pphead;
	
	while (pcur != NULL) {
		ListNode* delet = pcur;
		pcur = pcur->next;
		free(delet);
		delet = NULL;
	}
	*pphead = NULL;
	pcur = NULL;
}


//打印菜单
void printf_menu() {
	printf("=========================================================================\n");
	printf("有以下操作:\n");
	printf("插入操作:\n");
	printf("1.头部插入	2.尾部插入	3.pos位置之前插入	4.pos位置之后插入\n");
	printf("删除操作:\n");
	printf("5.头部删除	6.尾部删除	7.pos位置删除\n");
	printf("\n");
	printf("8.查找元素	9.修改元素\n");
	printf("=========================================================================\n");
	printf("\n");
}


//现在是主函数
int main() {
	ListNode* phead = NULL;
	Init_ListNode(&phead);
	ListNode* pphead = &phead;
	int choose = 0;
	do {
		system("cls");
		printf_menu();
		printf("当前的链表为:\n");
		my_printf(phead);
		printf("请输入你的选择(按-1退出程序):\n");
		scanf("%d", &choose);
		switch (choose) {
		case 1: {
			printf("请输入你想输入元素的个数:\n");
			int num = 0;
			scanf("%d", &num);
			Elemtype data = 0;
			printf("请输入你想输入的元素:\n");
			for (int i = 0; i < num; i++) {
				scanf("%d", &data);
				Push_Front(pphead, data);
			}
			printf("插入成功!!!\n");
			Sleep(2000);
			break;
		}
		case 2: {
			printf("请输入你想输入元素的个数:\n");
			int num = 0;
			scanf("%d", &num);
			Elemtype data = 0;
			printf("请输入你想输入的元素:\n");
			for (int i = 0; i < num; i++) {
				scanf("%d", &data);
				Push_Back(pphead, data);
			}
			printf("插入成功!!!\n");
			Sleep(2000);
			break;
		}
		case 3: {
			printf("请输入你想插入的位置:\n");
			int pos = 0;
			scanf("%d", &pos);
			printf("请输入你想插入的元素:\n");
			Elemtype data = 0;
			scanf("%d", &data);
			Push_pos_Front(pphead, data, pos);
			printf("插入成功!!!\n");
			Sleep(2000);
			break;
		}
		case 4: {
			printf("请输入你想插入的位置:\n");
			int pos = 0;
			scanf("%d", &pos);
			printf("请输入你想插入的元素:\n");
			Elemtype data = 0;
			scanf("%d", &data);
			Push_pos_Back(pphead, data, pos);
			printf("插入成功!!!\n");
			Sleep(2000);
			break;
		}
		case 5: {
			Pop_Front(pphead);
			printf("删除成功!\n");
			Sleep(2000);
			break;
		}
		case 6: {
			Pop_Back(pphead);
			printf("删除成功!\n");
			Sleep(2000);
			break;
		}
		case 7: {
			printf("请输入你想要删除元素的位置:\n");
			int pos = 0;
			scanf("%d", &pos);
			Pop_pos(pphead, pos);
			printf("删除成功!\n");
			Sleep(2000);
			break;
		}
		case 8: {
			printf("请输入你想要查找的元素:\n");
			Elemtype data = 0;
			scanf("%d", &data);
			Search_elem(pphead, data);
			Sleep(2000);
			break;
		}
		case 9: {
			printf("请输入你想要修改之后的元素:\n");
			Elemtype data = 0;
			scanf("%d", &data);
			printf("请输入想要改变的位置:\n");
			int pos = 0;
			scanf("%d", &pos);
			Change_elem_pos(pphead, pos, data);
			printf("修改成功!!!\n");
			Sleep(2000);
			break;
		}
		case -1: {
			printf("正在退出程序...\n");
			Sleep(1000);
			printf("退出成功!!!\n");
			Sleep(2000);
			break;
		}
		default: {
			printf("choose值不合法,请重新输入:\n");
			Sleep(2000);
			break;
		}
		}
	} while (choose != -1);
	Destory_ListNode(pphead);
	pphead = NULL;
	return 0;
}

以上就是我对单链表所有内容的分享了,感谢大佬们的阅读~~~

文章是自己写的哈,有啥描述不对的、不恰当的地方,恳请大佬指正,看到后会第一时间修改,感谢您的阅读。

相关推荐
牛三金2 小时前
魔改-隐语PSI通信,支持外部通信自定义
服务器·前端·算法
菜鸟233号2 小时前
力扣106 从中序与后序遍历序列构造二叉树 java实现
java·算法·leetcode
Donald_wsn2 小时前
牛客 栈和排序 C++
数据结构·c++·算法
沃达德软件2 小时前
智慧警务实战模型与算法
大数据·人工智能·算法·数据挖掘·数据分析
CryptoPP2 小时前
期货数据获取与可视化全攻略:从API对接至K线图生成
运维·服务器·开发语言·数据结构·金融
LYFlied2 小时前
LeetCode热题Top100:核心算法思想与前端实战套路
前端·算法·leetcode·面试·算法思想·算法套路·解题公式
coderxiaohan2 小时前
【C++】红黑树的实现
数据结构·c++·算法
纵有疾風起2 小时前
【C++—STL】哈希表底层封装与unorderedset/unorderedmap模拟实现
开发语言·数据结构·c++·stl·哈希算法·散列表
dangdang___go2 小时前
使用国产AI模型进行“委婉劝学程序”的模拟实现||创建可执行程序营造惊喜感
c语言·c++·豆包·劝学程序开发·创建可执行文件营造惊喜感