数据结构初阶(第六讲)单链表的功能实现

一. 初识单链表

链表是一种物理结构上非连续,非顺序的存储结构,数据元素的逻辑顺序 是通过链表中的指针链接次序实现的。(逻辑结构是线性的,物理结构不一定是线性的)

我们知道,火车由一节一节的车厢 组成,那么,链表由一个一个的结点 组成。其中,这个结点包括两部分:保存的数据和指针(始终保存下一个结点的地址)

注:链表每个结点在堆区是独立存在的,不是连续的!

(一)结点

与顺序表不同的是,链表里的每节"车厢"都是独立申请下来的空间,我们称之为:结点。图中指针变量plist保存的是第一个结点的地址,我们称plist此时"指向"第一个结点,如果我们希望plist指向第二个结点时,只需要修改plist保存的内容为0x0012FFA0。

链表中每个结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量来保存下一个结点的位置才能从当前结点找到下一个结点。

(二)链表的性质

①链式结构在逻辑上是连续的,在物理结构上不一定是连续的。

②结点一般是从堆上申请的。

③从堆上申请下来的空间,是按照一定策略分配出来的,每次申请的空间可能连续,可能不连续。

(三)链表的打印

给定的链表结构中,如何实现结点从头到尾的打印?

cpp 复制代码
void SLTprint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur != NULL)
	{
		printf("%d-> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

二. 单链表的尾插及测试

cpp 复制代码
//1.单链表的尾插
SLTNode* SLTbuynode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
SLTNode* SLTpushback(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	//申请新节点
	SLTNode* newnode=SLTbuynode(x);
	//判断链表是否为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* ptail = *pphead;
		while (ptail->next != NULL)
		{
			ptail = ptail->next;
		}
		//找到尾结点
		ptail->next = newnode;
	}
}
cpp 复制代码
void test02()
{
	//创建空链表
	SLTNode* plist = NULL;
	SLTpushback(&plist, 1);
	SLTpushback(&plist, 2);
	SLTpushback(&plist, 3);
	SLTpushback(&plist, 4);
	SLTPrint(plist);
}

三. 单链表的头插

cpp 复制代码
//2.单链表的头插
SLTNode* SLTpushfront(SLTNode** phead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLTbuynode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

四. 单链表的尾删

cpp 复制代码
// 3.单链表的尾删
SLTNode* SLTpopback(SLTNode** pphead)
{
	//链表为空不能删除
	assert(pphead && *pphead);
	//链表只有一个结点的情况
	if ((*pphead->next == NULL))
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* prev = NULL;
		SLTNode* ptail = *pphead;
		while (ptail->next)
		{

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

五. 单链表的头删

cpp 复制代码
//4.单链表的头删
void SLTpopfront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

六. 关于顺序表与链表问题的思考

1.中间/头部的插入删除,时间复杂度为O(N)。

2.增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

3.增容一般是呈2倍增长,势必会有一定的空间浪费。

但是链表头部插入删除,时间复杂度为O(1),并且不需要增容,不存在空间浪费。

所以 ,有时我们要根据不同的情形,来选择不同的数据结构。例如:如果一个场景频繁需要头插,则建议选择链表 更为合适;如果一个场景频繁需要尾插,则建议选择顺序表更为合适。


七. 单链表的查找

cpp 复制代码
//5.查找
SLTNode* SLTfind(SLTNode* phead, SLTDataType x)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
			return pcur;
		pcur = pcur->next;
	}
	return NULL;
}

八. 在指定位置之前插入数据

cpp 复制代码
//6.在指定位置之前插入数据
void SLTinsert(SLTNode** pphead, SLTDataType x, SLTNode* pos)
{
	assert(pphead && pos);
	STLNode* newnode = SLTbuynode(x);
	//若pos就是头结点,此时要当成头插处理
	if (pos == *pphead)
	{
		//头插
		SLTpushfront(pphead, x);
	}
	else
	{
		//找pos的前一个结点
		SLTNode* prev = *pphead;
		while (*prev->next != pos)
		{
			prev = prev->next;
		}
		//找到了,开始牵手
		prev->next = newnode;
		newnode->next = pos;
	}
}

九. 在指定位置之后插入数据

cpp 复制代码
//7. 在指定位置之后插入数据
void SLTinsertafter(SLTDataType x, SLTNode* pos)
{
	assert(pos);
	STLNode* newnode = STLbuynode(x);
	newnode->next = pos->next;
	pos->next->newnode;
}

十. 删除pos结点

cpp 复制代码
//8.删除pos结点
void SLTerase(SLTNode** pphead, SLTNode* pos)
{
	//pos刚好就是头结点
	if (pos == *pphead)
	{
		SLTpopfront(pphead);
	}
	else
	{
		assert(pphead && pos);
		SLTNode* pev = *pphead;
		while (pev->next != pos)
		{
			pev = pev->next;
		}
		pev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

十一. 删除pos位置之后的一个结点

cpp 复制代码
//9.删除pos位置之后的一个结点
void SLTeraseafter(SLTNode* pos)
{
	assert(pos&&pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

十二.销毁链表

cpp 复制代码
//10.销毁链表
void SLTdestory(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		free(pcur);
		pcur = next;
	}
   *pphead=NULL;
}

以上就是今天的内容,下一节我将讲述单链表的应用,喜欢的朋友们可以一键三连~

相关推荐
CS创新实验室4 小时前
一棵树怎么做到“自平衡还不太费力“?——红黑树的旋转与染色
数据结构·算法·红黑树·408·计算机考研
Logic1014 小时前
C语言/数据结构位运算题解:异或XOR找出被挑选一次的果子编号——只出现一次的数字
c语言·数据结构·算法·数组·位运算·时间复杂度·空间复杂度
从小白到软工6 小时前
4-3 项目-学生成绩管理系统(上)
c语言·开发语言·数据结构·经验分享·ai编程
悠仁さん6 小时前
非类型模板参数的特化与偏特化
java·数据结构·算法
haluhalu.6 小时前
深入理解 AI Agent(1.2):Harness 工程——模型之外的竞争力
前端·数据结构·人工智能·python·langchain·个人开发
垆边人似月.7 小时前
幻兽防御战(贪心算法)
数据结构·算法
Cccp.1238 小时前
【leetcode】(十三)大数据处理
数据结构·算法·leetcode
Pniubi9 小时前
力扣70爬楼梯
数据结构·算法·leetcode
雪落漂泊11 小时前
C++算法:双指针篇(下)
数据结构·c++·算法
学逆向的21 小时前
C 循环结构的执行效率分析
c语言·开发语言·数据结构·网络安全