【数据结构与算法】(5):超详细的单链表完整实现(精美图解+完整代码)

🤡博客主页:醉竺

🥰本文专栏:《数据结构与算法》

😻欢迎关注:感谢大家的点赞评论+关注,祝您学有所成!


✨✨💜💛想要学习更多数据结构与算法点击专栏链接查看💛💜✨✨ 


目录

一.线性表的链式表示

1.1 引言

1.2 定义 

1.3 单链表的存储结构图示 

二.单链表的基本操作 (代码)

2.1 单链表的准备工作 

2.1.1 结点的存储结构 

2.1.2 开辟新结点

2.1.3 遍历打印

2.2 单链表的插入

2.2.1 头插 

2.2.2 尾插 

2.3 单链表的删除 

2.3.1 头删

2.3.2 尾删​

2.4 单链表结点的查找 

2.5 单链表结点的修改

2.6 单链表任意位置的插入 

2.6.1 结点在指定位置前插入 

2.6.2 结点在指定位置后插入 

2.7  删除指定位置的结点

2.7.1 删除指定位置前的结点 

2.7.2 删除指定位置后的结点

2.8 单链表的销毁

三.归纳总结


一.线性表的链式表示

链表的介绍:

与顺序表相同,链表也是一种线性表,它的数据的逻辑组织形式是一维的。而与顺序表不同的是,链表的物理存储结构是用一组地址任意的存储单元存储数据的。也就是说,它不像顺序表那样占据一段连续的内存空间,而是将存储单元分散在内存的任意地址上。在链表结构中,每个数据元素记录都存放到链表的一个结点 (node)中,而每个结点之间由指针将其连接在一起,这样就形成了一条如同“链”的结构。

a7bc0169901c4c16ba4b350a359cc886.png

1.1 引言

在C程序中,链表的每个结点可以是一个结构体类型元素,当然也可以是其他的构造类型元素。在链表的每个结点中,都必须有一个专门用来存放指针(地址)的域,用这个指针域来存放后继结点的地址,这样就达到了连接后继结点的目的。此外,一条链表的最后一个结点的指针域要置空,表示该结点为链表的尾结点,因为它没有后继结点了。

单向链表(SinglyLinked List)是列表中最常用的一种,它就像火车,所有节点串成一列而且指针所指的方向一样。也就是链表表中每个数据除了要存储原本的数据,还必须存储下一个数据的存储地址。

4666b389877a484abb34a0145c818bee.jpeg

1.2 定义 

单链表是通过一组任意的存储单元(不要求连续)来存储线性表中的数据元素,对每个元素除了存放自身信息之外,还需要存放一个指向其后继的指针。其中data为数据域,存放本结点的数据元素;next为指针域,存放其后继结点的地址。                                                           

1.3 单链表的存储结构图示 

单链表结点存储结构示意图 

03e6b5c322ed4d3bb17b24dc12880c9c.png

单链表存储结构逻辑图 

9c7a39ce37f5403598131f384b26e022.png

单链表的存储结构物理图 (重要)​​​​​​​

75fbb3a6da0a42f18075ac6a73707e53.png

从上述图片可以看出单链表有以下特征: 

1.每一个结点包括两部分:数据域和指针域。其中数据域用来存放数据元素本身的信息,指针域用来存放后继结点的地址。

2.单链表逻辑上是连续的,而物理上并不一定连续存储结点。

3.单链表是非随机存取的存储结构,查找某个结点时需要从表头遍历,只要获得链表的第一个结点,就可以通过头指针遍历整条链表。

4.解决了顺序表插删需要大量移动元素的缺点。

5.最后一个结点的next指向“空”(通常用NULL或“^”符号表示)。


二.单链表的基本操作 (代码)

2.1 单链表的准备工作 

2.1.1 结点的存储结构 

以C语言结构体类型来储存单链表结点。 

typedef int SLTDateType;

//定义单链表结点的存储结构
typedef struct SListNode
{
	SLTDateType data; //数据域
	struct SListNode* next; //指针域
}SListNode;

2.1.2 开辟新结点

 把“开辟新结点”方法封装起来,省去后续其它操作方法中再开辟新结点的重复冗余操作。

// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

2.1.3 遍历打印

// 单链表打印
void SListTPrint(SListNode* phead)
{
	SListNode* cur = phead; //定义一个cur指针从头结点开始往后遍历

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next; //cur指针往后移动
	}
	printf("NULL\n");
}

2.2 单链表的插入

2.2.1 头插 

分解过程: 

ef4b37428abc49c89d88493101aba88b.png

原理:

1. 从一个空表开始,先生成一个新结点 ,将读取到的数据X存入新结点的数据域data中,并用指针newnode指向该新结点。

2. 将新结点的指针域next指向当前链表的表头,即把新结点插入到表头。

3. 再把头指针移动(指向)到新结点上即可。

注:采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的顺序是相反的。每个结点插入的时间为 O(1),设单链表长为n,则总时间复杂度为 O(n)。 

头插法建立单链表:读入数据的顺序与生成的链表中元素的顺序是相反的

// 单链表头插
void SListPushFront(SListNode** pphead, SLTDateType x)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	//assert(*pphead); //*pphead不能断言,链表为空也需要能插入

	SListNode* newnode = BuySListNode(x);

	newnode->next = *pphead;
	*pphead = newnode;

    // 单链表头插时,链表是否为空不影响头插,操作是统一的
	/*
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	*/
}

函数形参是二级指针解释:
头插法的操作过程中是需要改变头指针head的,因为每插入一个新结点,head最后就会指向新结点。如果函数形参是一级指针phead的话,当头指针实参head传给形参phead后,形参phead只是实参上数值的一份拷贝,当形参头指针phead移动且指向的内容更改时,真正的头指针实参head的内容并没有改变。所以必须用二级指针pphead,实参应传入头指针head的地址即&head才符合要求;如果是要改变结构体的指向,即改变的是结构体的内容,只需要传入一级指针即可。后续操作同理,谨记!

理解:

如果需要在一个除主函数main外的函数内部改变一个变量的内容,就需要这个变量的地址。即,想要改变谁,就需要在该函数内传入谁的地址。

例如,要改变一个int型变量的内容,在函数内部就需要传入这个int变量的地址,然后再解引用来更改这个变量其中的内容。

同理,如果要改变的是一个指针变量的内容呢,就需要这个指针变量的地址,也就是二级指针。

通过获取一个变量的地址,再对其解引用,从而操控这个变量的内容,这也就是指针的魅力!指针的强大! 

2.2.2 尾插 

 ab7eb1457ed84d4c91aed40035f6cfd4.png

e6b998d6c1e24ca18612f665af2ba326.png

原理: 

1.空链表情况:当前只有一个头指针无任何链表结点,生成一个新结点,头指针直接指向这个新结点。

2.非空链表:建立一个尾指针指向单链表最后一个结点,生成一个新结点,让尾结点的指针域next指向新结点,然后尾指针再移动(指向)到新结点上。

注:单链表规定尾结点的next域必须为空。尾插法的关键是找到尾结点,让尾结点的next指针域指向新的要插入的结点,然后尾指针再指向新结点(也是新的尾结点)。

尾插法建立单链表:读入数据的顺序与生成的链表中元素的顺序是相同的。

// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	//assert(*pphead); //*pphead不能断言,链表为空也需要能插入
	SListNode* newnode = BuySListNode(x);

	//1.空链表情况:当前只有一个头指针无任何链表结点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else //2.非空链表
	{
		SListNode* tail = *pphead;

		/*
		tail->next != NULL : 使tail指针指向尾结点(即最后一个结点)的时候停下,目的是在尾结点后面插入新结点
		因为尾插法的关键就是让尾结点的next指针指向新的要插入的结点,所以需要先遍历到尾结点
		*/
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

2.3 单链表的删除 

2.3.1 头删

1f1f98c3bb674a8ca115bb10719d14ee.png

1.创建一个del指针,用来记录当前链表要删除的第一个结点。

2.头指针head向后移动一位,指向后面一个结点(新的表头结点)。

3.释放free要删除的第一个结点(旧的表头结点)。 

// 单链表头删
void SListPopFront(SListNode** pphead)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	assert(*pphead); //头指针为NULL,代表空链表,此时删除无意义

	// if ((*pphead)->next == NULL) //一个结点
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	//else //多个结点
	//{
	//	SListNode* del = *pphead;
	//	//*pphead = del->next;
	//	*pphead = (*pphead)->next;
	//	free(del);
	//	del = NULL;
	//}

	//统一写法
	SListNode* del = *pphead; //记录要删除的第一个结点
	//*pphead = del->next;
	*pphead = (*pphead)->next;
	free(del);
	del = NULL;
}

2.3.2 尾删996a541481ac4b3e88659708aa4f901a.png

9cfbd048511b45a085f77085240ac4f8.png

2be7e0c9bfae4ea784d133e03586829c.png

原理:

1.当链表只有一个结点的时候:直接释放这个结点即可。

2.当链表有多个结点的时候:尾删法的关键是要找到尾结点的前一个结点,找到尾结点的前一个结点,也就相应的找到了尾结点(prve→next就找到了尾结点),然后删除尾结点。再把新的尾结点的next指针置空(因为单链表最后一个结点的指针域要指向空)。

方法一方法二本质上是一样的。

// 单链表尾删
void SListPopBack(SListNode** pphead)
{
	assert(pphead); //链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); //头指针为NULL,代表空链表,此时删除无意义

	if ((*pphead)->next == NULL) //一个结点
	{
		free(*pphead);
		*pphead = NULL;
	}
	else //多个结点
	{
		//第一种方法
		/*
		SListNode* tail = *pphead;
		SListNode* prev = NULL;

		while (tail->next != NULL) //循环条件为要找到尾结点,即指针指向尾结点停止时
		{
			prev = tail; //记录尾结点的前一个结点
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
		*/
		//第二种方法
		SListNode* tail = *pphead;
		// 找尾
		while (tail->next->next) //找到单链表倒数第二个结点,即尾结点前一个结点
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

2.4 单链表结点的查找 

遍历整个链表用cur指针记录当前指向的结点,当结点的数据域data与所要查找的数据一样时返回当前结点(指针)。

// 单链表结点的查找
SListNode* SListFind(SListNode* phead, SLTDateType x)
{
	SListNode* cur = phead;

	while (cur != NULL)
	{
		if (x == cur->data)
			return cur;
		else
			cur = cur->next;
	}

	return NULL;
}

2.5 单链表结点的修改

// 单链表结点的修改
void SListModify(SListNode* phead, SLTDateType x, SLTDateType y)
{
	SListNode* pos = SListFind(phead, x);
	if (pos != NULL)
		pos->data = y;
}

2.6 单链表任意位置的插入 

2.6.1 结点在指定位置前插入 

516415ed5e1341309e9c89e7bb11e3d6.png

bcedd5bf55404ca88226ad7f4e0a4971.png

原理: 

1.若是在第一个结点前一个位置插入新的结点,直接调用头插法即可。

2.在其它非第一个结点位置前插入新结点,新结点的指针域指向pos位置的结点,pos位置前一个结点的指针域指向新结点。

// 单链表结点在pos前一个位置插入
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next; //prev指向pos前一个位置的结点
		}

		SListNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

2.6.2 结点在指定位置后插入 

38f07b0503bd4f719c58903f1c228c54.png

 原理:

1.生成一个新结点,新结点的指针域指向pos位置后面的一个结点,处于pos位置的结点指针域指向新结点。

2.步骤1和步骤2的顺序不可逆。因为如果pos位置的结点指针域先指向新结点,(此时pos位置结点的指针域不在记录其原先后面一个结点的位置了),就没法找到pos位置后面的一个结点,新结点也没法插入了。

// 单链表结点在pos后一个位置插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

2.7  删除指定位置的结点

2.7.1 删除指定位置前的结点 

1cc2d3f7ac1642b3bc14496b6880ee2c.png

002a0f059abe4db3a4f15db7ab92c96c.png

原理:

1.移除头结点和移除其他节点的操作是不一样的,因为删除链表的其它节点都是通过找到它的前一个结点,然后其前一个结点再指向当前结点的后一个结点,从而来移除当前结点。

2.而头结点没有前一个节点。所以头结点如何移除呢?其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点。 即头删法。

3.要删除pos位置的结点,让pos位置前一个结点的指针指向pos位置后一个结点,再释放pos位置的结点。

// 删除pos位置的结点
void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

2.7.2 删除指定位置后的结点

a9076f3c8beb4740be37efe3210a2abc.png

// 删除pos位置后面的结点
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next); //如果pos后面没有结点(即pos位置是尾结点),就不能再删除pos后面的结点了

	SListNode* after = pos->next;
	pos->next = after->next;
	free(after);
	after = NULL;
}

2.8 单链表的销毁

原理:

依次遍历每一个结点 ,用cur指针记录当前结点,next指针记录当前结点的下一个结点。然后释放当前结点,cur再移动指向next指针指向的结点,直到遍历完整个链表。

// 单链表的销毁
void SListDestroy(SListNode** pphead)
{
	assert(pphead);

	SListNode* cur = *pphead;
	while (cur)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

单链表的内容学习结束,感兴趣的可以学习下一篇内容:
​​​​​​​《带头结点的双向循环链表》icon-default.png?t=N7T8https://blog.csdn.net/weixin_43382136/article/details/130907108

三.归纳总结

把单链表的所有代码整合一块,可以运行跑起来,需要的直接复制即可;创建三个文件,SList.h,SList.c,Test.c .

1.SList.h 

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDateType;

//定义单链表结点的存储结构
typedef struct SListNode
{
	SLTDateType data; //数据域
	struct SListNode* next; //指针域
}SListNode;

// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);

// 单链表打印
void SListTPrint(SListNode* phead);

// 单链表头插
void SListPushFront(SListNode** phead, SLTDateType x);
// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x);

// 单链表头删
void SListPopFront(SListNode** pphead);
// 单链表尾删
void SListPopBack(SListNode** pphead);

// 单链表结点的查找
SListNode* SListFind(SListNode* phead, SLTDateType x);
// 单链表结点的修改
void SListModify(SListNode* phead, SLTDateType x, SLTDateType y);

// 单链表结点在pos前一个位置插入
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLTDateType x);
// 单链表结点在pos后一个位置插入
void SListInsertAfter(SListNode* pos, SLTDateType x);

// 删除pos位置的结点
void SListErase(SListNode** pphead, SListNode* pos);
// 删除pos位置后面的结点
void SListEraseAfter(SListNode* pos);

// 单链表的销毁
void SListDestroy(SListNode** pphead);

2.SList.c 

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"

// 单链表打印
void SListTPrint(SListNode* phead)
{
	SListNode* cur = phead; //定义一个cur指针从头结点开始往后遍历

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next; //cur指针往后移动
	}
	printf("NULL\n");
}

// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

// 单链表头插
void SListPushFront(SListNode** pphead, SLTDateType x)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	//assert(*pphead); //*pphead不能断言,链表为空也需要能插入

	// 单链表头插时,链表是否为空不影响头插,操作是统一的
	/*
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	*/
	SListNode* newnode = BuySListNode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

// 单链表尾插
void SListPushBack(SListNode** pphead, SLTDateType x)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	//assert(*pphead); //*pphead不能断言,链表为空也需要能插入
	SListNode* newnode = BuySListNode(x);

	//1.空链表情况:当前只有一个头指针无任何链表结点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else //2.非空链表
	{
		SListNode* tail = *pphead;

		/*
		tail->next != NULL : 使tail指针指向尾结点(即最后一个结点)的时候停下,目的是在尾结点后面插入新结点
		因为尾插法的关键就是让尾结点的next指针指向新的要插入的结点,所以需要先遍历到尾结点
		*/
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

// 单链表头删
void SListPopFront(SListNode** pphead)
{
	assert(pphead); //链表为空,pphead也不能为空,因为它是头指针plist的地址
	assert(*pphead); //头指针为NULL,代表空链表,此时删除无意义

	// if ((*pphead)->next == NULL) //一个结点
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	//else //多个结点
	//{
	//	SListNode* del = *pphead;
	//	//*pphead = del->next;
	//	*pphead = (*pphead)->next;
	//	free(del);
	//	del = NULL;
	//}

	//统一写法
	SListNode* del = *pphead; //记录要删除的第一个结点
	//*pphead = del->next;
	*pphead = (*pphead)->next;
	free(del);
	del = NULL;
}

// 单链表尾删
void SListPopBack(SListNode** pphead)
{
	assert(pphead); //链表为空,pphead也不为空,因为他是头指针plist的地址
	assert(*pphead); //头指针为NULL,代表空链表,此时删除无意义

	if ((*pphead)->next == NULL) //一个结点
	{
		free(*pphead);
		*pphead = NULL;
	}
	else //多个结点
	{
		//第一种方法
		/*
		SListNode* tail = *pphead;
		SListNode* prev = NULL;

		while (tail->next != NULL) //循环条件为要找到尾结点,即指针指向尾结点停止时
		{
			prev = tail; //记录尾结点的前一个结点
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
		*/
		//第二种方法
		SListNode* tail = *pphead;
		// 找尾
		while (tail->next->next) //找到单链表倒数第二个结点,即尾结点前一个结点
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

// 单链表结点的查找
SListNode* SListFind(SListNode* phead, SLTDateType x)
{
	SListNode* cur = phead;

	while (cur != NULL)
	{
		if (x == cur->data)
			return cur;
		else
			cur = cur->next;
	}

	return NULL;
}

// 单链表结点的修改
void SListModify(SListNode* phead, SLTDateType x, SLTDateType y)
{
	SListNode* pos = SListFind(phead, x);
	if (pos != NULL)
		pos->data = y;
}

// 单链表结点在pos前一个位置插入
void SListInsertBefore(SListNode** pphead, SListNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next; //prev指向pos前一个位置的结点
		}

		SListNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

// 单链表结点在pos后一个位置插入
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

// 删除pos位置的结点
void SListErase(SListNode** pphead, SListNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

// 删除pos位置后面的结点
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next); //如果pos后面没有结点(即pos位置是尾结点),就不能再删除pos后面的结点了

	SListNode* after = pos->next;
	pos->next = after->next;
	free(after);
	after = NULL;
}

// 单链表的销毁
void SListDestroy(SListNode** pphead)
{
	assert(pphead);

	SListNode* cur = *pphead;
	while (cur)
	{
		SListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

 3.Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SList.h"

//测试头插和尾插法
void TestSListist1()
{
	SListNode* plist = NULL;
	printf("单链表头插4个元素后:\n");
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListTPrint(plist);

	printf("单链表再尾插2个元素后:\n");
	SListPushBack(&plist, 5);
	SListPushBack(&plist, 6);
	SListTPrint(plist);

	/*printf("把data为4的结点修改成data为666后:\n");
	SListModify(plist, 4, 666);
	SListTPrint(plist);*/

	printf("在data为3的结点前面插入data为888的结点后:\n");
	SListNode* pos = SListFind(plist, 3);
	if (pos != NULL)
	{
		SListInsertBefore(&plist, pos, 888);
	}
	SListTPrint(plist);

	printf("在data为5的结点后面插入data为999的结点后:\n");
	pos = SListFind(plist, 5);
	if (pos)
	{
		SListInsertAfter(pos, 99);
	}
	SListTPrint(plist);

	printf("单链表再尾删2个元素后:\n");
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListTPrint(plist);

	printf("单链表再头删2个元素后:\n");
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListTPrint(plist);

	//销毁链表
	SListDestroy(&plist);
}

int main()
{
	TestSListist1();
	return 0;
}

创作不易,如果本文对您有点帮助,希望点个赞或者关注吧~ 

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醉竺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值