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

一. 初识单链表

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

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

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

(一)结点

与顺序表不同的是,链表里的每节"车厢"都是独立申请下来的空间,我们称之为:结点。图中指针变量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;
}

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

相关推荐
2401_841495642 小时前
【数据结构】链栈的基本操作
java·数据结构·c++·python·算法·链表·链栈
Archie_IT2 小时前
「深入浅出」嵌入式八股文—P2 内存篇
c语言·开发语言·数据结构·数据库·c++·算法
是那盏灯塔2 小时前
【算法】——动态规划算法及实践应用
数据结构·c++·算法·动态规划
MATLAB代码顾问3 小时前
MATLAB计算标准径流指数(Standard Runoff Index,SRI)
数据结构·算法·matlab
想ai抽4 小时前
吃透大数据算法-霍夫曼编码(Huffman Coding)
大数据·数据结构·算法
Miraitowa_cheems6 小时前
LeetCode算法日记 - Day 63: 图像渲染、岛屿数量
java·数据结构·算法·leetcode·决策树·贪心算法·深度优先
拾光Ծ7 小时前
【数据结构】二叉搜索树 C++ 简单实现:增删查改全攻略
数据结构·c++·算法
小贾要学习7 小时前
编程中常见的排序算法
数据结构·c++·算法·排序算法
Yupureki7 小时前
从零开始的C++学习生活 4:类和对象(下)
c语言·数据结构·c++·学习