数据结构初阶之单链表

今天要说的是单链表的内容,目录在下面先请大家看看看:

一、单链表的概念

概念:单链表是⼀种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表
中的指针链接次序实现的 。
简单来理解就是,链表是一种物理结构 不一定连续 ,但是逻辑结构 一定是连续 的一种数据结构,为什么这么说呢,其实是因为它的结构所导致的,在下面小编会告诉大家单链表的结构,所以先不用担心,单链表也是数据结构的链表中最简单的,所以现在大家请跟着我一起学习单链表。
二、 单链表结构的介绍
//单链表的结构
typedef int SLDataType;
typedef struct ListNode
{
	SLDataType a;   //保存的数据
	struct ListNode* next;  //储存的下一个节点地址的指针
}SL;
可能这样表示刚入门的小伙伴肯定有点看不明白,所以我画了一个更加生动的图来表明它是如何把数据串起来,并且来表明物理结构不一定连续,但是逻辑结构一定连续,请看下图:
相信大家到这已经对单链表的结构有一定的认识了吧,其实也就是两个元素,一个数据内容,一个结构体指针,就可以构成单链表。相信大家已经迫不及待想知道如何实现单链表吧,接下来我们继续往下看。
三、 单链表的实现
对于单链表的实现我们要学会以下的函数,就可以做到对单链表的增删查改,并且基本掌握单链表,请看下表:
//单链表的初始化
void SLInit(SL** sl);
//单链表的尾部插入数据
void SLBackPush(SL** sl,SLDataType x);
//单链表的头部插入数据
void SLFrontPush(SL** sl, SLDataType x);
//单链表尾部数据的删除
void SLBackDel(SL** sl);
//单链表头部数据的删除
void SLFrontDel(SL** sl);
//单链表数据的查找
SL* SLFindData(SL** sl, SLDataType x);
//单链表指定位置前的插入数据
void SLPosiFrontPush(SL** sl, SL* pos, SLDataType x);
//单链表指定位置之后的插入数据
void SLPosiBackPush(SL** sl, SL* pos, SLDataType x);
//单链表指定位置的数据删除
void SLPosiDel(SL** sl, SL* pos);
//单链表的销毁
void SLDestroy(SL** sl);
首先,创建一个结构体指针,并且 初始化
	SL* sl;//先创建一个结构体指针,准备用来接收第一个节点的地址
	//因为 sl的地址是需要存第一个节点的地址,会被改变,所以我们传的是sl指针的地址
	SLInit(&sl);
初始化后 sl 作为 存放头节点的指针
//单链表的初始化
void SLInit(SL** sl)
{
	//把头节点先置为空
	*sl = NULL;
}
接下来是单链表的尾部插入数据( 尾插):
//单链表的尾部插入数据
void SLBackPush(SL** sl, SLDataType x)
{
	//如果收节点是空的说明是第一次放入数据
	if (*sl == NULL)
	{
		SL* tem = (SL*)malloc(sizeof(SL));
		if (tem == NULL)
		{
			perror("malloc");
			exit(1);
		}
		*sl = tem;
		(*sl)->a = x;
		(*sl)->next = NULL;
	}
	//如果不是第一次放入数据,则把要放入的数据放到最后
	else
	{
		//先要找到尾结点
		SL* cur = *sl;
		while (cur->next)
		{
			cur = cur->next;
		}
		//开辟节点,并放入数据到新的节点中去
		SL* tem = (SL*)malloc(sizeof(SL));
		if (tem == NULL)
		{
			perror("malloc");
			exit(2);
		}
		tem->a = x;
		tem->next = NULL;
		//把新的节点和单链表连接起来
		cur->next = tem;
	}
}
单链表的头部插入数据( 头插):
//单链表的头部插入数据
void SLFrontPush(SL** sl, SLDataType x)
{
	//和尾插一样先判断单链表是否为空,如果为空就和尾插的实现一样
	if (*sl == NULL)
	{
		SL* tem = (SL*)malloc(sizeof(SL));
		if (tem == NULL)
		{
			perror("malloc");
			exit(1);
		}
		*sl = tem;
		(*sl)->a = x;
		(*sl)->next = NULL;
	}
	else
	{
		//单链表中原本就有数据来实现头插
		SL* tem = (SL*)malloc(sizeof(SL));
		if (tem == NULL)
		{
			perror("malloc");
			exit(1);
		}
		//先把数据放到这个新的节点中
		tem->a = x;
		//在用这个节点作为新的头节点连接上一个头节点
		tem->next = *sl;
		//接下来把原来的头节点换成 tem (新的头节点);
		*sl = tem;
	}
}

单链表尾部数据的删除(尾删),请先看原理图,在实现代码:

//单链表尾部数据的删除
void SLBackDel(SL** sl)
{
	//要实现尾部数据的删除现需要判断单链表里面是否有数据才可以实现删除
	//所以先需要断言一下,单链表不为空,如果单链表为空,则不能实现数据的删除
	assert(*sl);
	//尾部数据的删除,需要先找到尾部数据在实现删除即可
	SL* cur = *sl;
	while (cur->next->next)
	{
		cur = cur->next;
	}
	SL* del = cur->next;
	cur->next = NULL;
	free(del);
	del = NULL;
}

单链表头部数据的删除(头删):

//单链表头部数据的删除
void SLFrontDel(SL** sl)
{
	//和尾删一样需要先判断单链表中是否有数据才可以实现头删
	//先断言一下,看单链表是否为空
	assert(*sl);
	//先保存准备删除的头节点
	SL* cur = *sl;
	//然后让头节点连接后面的元素从而实现头删
	*sl = (*sl)->next;
	//在释放掉以前的头节点即已经完成头删
	free(cur);
	cur = NULL;
}

有了单链表数据的查找,才能实现后面的单链表特定位置数据的插入和删除,因为这个函数返回的指针就是特定位置的地址:

//单链表数据的查找
SL* SLFindData(SL** sl, SLDataType x)
{
	SL* cur = *sl;
	//遍历单链表找到这个数据
	//如果没找到特定的数据就返回空指针
	while (cur->a != x && cur != NULL)
	{
		cur = cur->next;
	}
	//此时的cur指针指向的就是数据的指定节点位置
	//直接返回这个节点的地址即可
	return cur;
}

特定数据位置之前的插入数据,请先看原理图,在来实现特定位置之前的数据插入:
//单链表指定位置前的插入数据
void SLPosiFrontPush(SL** sl, SL* pos, SLDataType x)
{
	//指定位置的插入数据必须链表不为空,如果为空就不能实现特定位置的数据插入
	//所以需要断言链表不为空
	assert(*sl);
	assert(pos);
	//找到pos之前的地址
	SL* cur = *sl;
	while (cur->next != pos)
	{
		cur = cur->next;
	}
	//再实现数据的插入
	SL* ins = (SL*)malloc(sizeof(SL));
	if (ins == NULL)
	{
		perror("malloc");
		exit(1);
	}
	//先把数据放大这个新开辟的空间中
	ins->a = x;
	//再把节点连接起来就可以完成
	cur->next = ins;
	ins->next = pos;
}

特定数据位置之后的数据插入,先看原理图再来实现代码:

//单链表指定位置之后的插入数据
void SLPosiBackPush(SL** sl, SL* pos, SLDataType x)
{
	//先得判断链表不为空才可以插入
	//特定的位置也不能为空
	assert(*sl);
	assert(pos);
	SL* cur = (SL*)malloc(sizeof(SL));
	if (cur == NULL)
	{
		perror("malloc");
		exit(1);
	}
	//把要插入的数据放入新的节点之中
	cur->a = x;
	//先存下pos之后的节点,为一会连接新的节点做铺垫
	SL* pnext = pos->next;
	//连接节点
	pos->next = cur;
	cur->next = pnext;
}
单链表特定位置的数据的删除
//单链表指定位置的数据删除
void SLPosiDel(SL** sl, SL* pos)
{
	//先得判断链表不为空,和指定位置不为空
	assert(pos);
	assert(*sl);
	//先得存下pos之前的节点,为后面删除节点做准备
	SL* cur = *sl;
	while (cur->next != pos)
	{
		cur = cur->next;
	}
	//删除节点的操作
	cur->next = pos->next;
	free(pos);
	pos = NULL;
}

单链表的销毁,先看原理图,在实现代码:

//单链表的销毁
void SLDestroy(SL** sl)
{
	//先得判断链表是否为空
	assert(*sl);
	SL* cur = *sl;
	//遍历链表,实现链表的销毁
	while (cur->next)
	{
		SL* pnext = cur->next;
		free(cur);
		cur = pnext;
	}
	//链表释放完之后再将结构体指针置为空即可完成操作
	(*sl) = NULL;
}

好了,以上就是单链表的实现,通过注释原理图的讲解相信大家已经对单链表有一定的认识,希望大家明白了之后可以自己实现一下单链表,好记性不如烂笔头,多实践才是刚入门的同学们应该做的。

四、单链表的优缺点

通过对单链表的实现,大家可以明显的感觉到链表的优缺点(相对于顺序表),接下来我来给大家一个完整的总结

优点:1、单链表不会存在空间的浪费,它是存储数据才会开辟空间。

           2、单链表中 任意位置的数据插入很简单
缺点:1、单链表 不支持下标访问
           2、单链表的 cpu高速缓存命中率低。(这一点先不急理解,知道有这个就行)
  • 15
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值