数据结构第三讲:单链表的实现

1.什么是单链表

单链表也是顺序表的一种,它在逻辑结构上是连续的,在物理结构上不一定是连续的

就像是火车一样,有一个火车头、很多节车厢,每相邻的车厢会通过钩子进行链接,单链表中,一节一节的车厢被成为是一个一个的节点,就像这样:
在这里插入图片描述

2. 节点

那么每一个节点到底是怎么样来链接的呢?其实每一个节点中只存储两个数据:需要存储的数据和指向下一个节点的指针,这样通过指针就能够找到下一个节点了

3.单链表的实现

3.1节点的结构

节点只需要存储两个内容,所以实现起来也会比较轻松:

typedef int SLTDateType;

typedef struct SListNode
{
	//存储数据
	SLTDateType date;
	//存储框架
	struct SListNode* next;
}SLTNode;

3.2打印单链表

我们先使用节点结构体自行创建一些节点,然后自行将节点链接起来,如下:

	//通过动态内存分配创建节点
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	node1->date = 1;

	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	node2->date = 2;

	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	node3->date = 3;

	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
	node4->date = 4;

	SLTNode* node5 = (SLTNode*)malloc(sizeof(SLTNode));
	node5->date = 5;

	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = node5;
	node5->next = NULL;

然后我们实现一下链表的打印功能:
打印我们使用一个while循环即可,因为链表最后一个节点指向的空间为NULL,所以我们可以以此为一个突破点,作为循环的终止条件,实现如下:

void SLTPrint(SLTNode* phead)
{
	//对于打印,使用一个循环即可
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->date);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

3.3申请一个新节点

上述我们自行申请的一个新节点太过于费事费力了,我们应该试着写一个函数,让函数来帮我们申请空间,实现起来也不困难:

//申请一个新节点
SLTNode* SLBuyNode(SLTDateType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));

	if (node == NULL)
	{
		perror("malloc faile!");
		exit(1);
	}
	node->date = x;
	node->next = NULL;
	return node;
}

3.4单链表尾部插入

看图:
在这里插入图片描述
我们只需要找到链表的末端,将最后一个节点中的地址指向新的节点,然后将新的节点中的指针指向NULL即可:

//单链表尾部插入
//注意:这里要使用二级指针,因为如果链表中没有节点时,我们要改变头指针的值,让它指向第一个节点的地址
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	//对于插入,首先都要先创建一个节点,才能够实现插入
	SLTNode* newnode = SLBuyNode(x);

	//插入方法
	//1.找到最后一个节点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
	}
}

3.5单链表头部插入

在这里插入图片描述
只需要将刚开辟节点中的指针指向原来的头节点地址,并改变头节点的指向即可:

//单链表头部插入
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = SLBuyNode(x);

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

3.6单链表的尾部删除

在这里插入图片描述
只需要将最后一个节点的空间释放掉,然后将释放后的最后一个节点中的指针指向NULL即可,需要注意的是:这里需要找到释放位置前的节点,当只存在一个节点时,最后一个节点前的空间是不能够进行访问的,这里需要单独讨论:

//单链表尾部删除
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);

	//将只有一个节点的情况单独讨论
	if ((*pphead)->next == NULL)//这里要注意:->的优先级要大于* 所以要将*pphead加上小括号
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//先找到最后一个数据的前一个数据
		SLTNode* pcur = *pphead;
		SLTNode* prev = NULL;
		while (pcur->next)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		prev->next = NULL;
		free(pcur);
		pcur = NULL;
	}
}

3.7单链表头部删除

在这里插入图片描述
头部删除相对简单,只需要释放空间,然后改变头指针的位置即可:

//单链表头部删除
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* prev = (*pphead)->next;
	free(*pphead);
	*pphead = prev;
}

3.8查找

查找查找的是数据所在的位置,返回的是节点的地址:

//查找
SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
	assert(phead);

	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->date == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

3.9在指定位置之前插入数据

在这里插入图片描述
在指定位置之前插入数据,需要找到指定位置前的那个节点,这里还需要单独讨论,因为当只存在一个节点时,前面的空间是无法访问的,和上边一样,然后我们只需要改变节点中指针的指向即可

//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead,SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(pos);

	//当在第一个节点前进行插入时
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		//先创建一个节点
		SLTNode* newnode = SLBuyNode(x);

		SLTNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = newnode;
		newnode->next = pos;
	}
}

3.10在指定位置之后插入数据

在这里插入图片描述
在指定位置之后插入数据,因为已经知道了在哪个位置进行插入,所以我们就能够直接找到下一个节点的地址,直接改变地址指向即可:

//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);

	//先创建一个节点
	SLTNode* newnode = SLBuyNode(x);

	newnode->next = pos->next;
	pos->next = newnode;
}

3.11删除pos节点

在这里插入图片描述
删除节点,只需要释放空间,改变指针指向即可:

//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(*pphead && pphead);

	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* pcur = *pphead;
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

3.12删除pos之后的节点

在这里插入图片描述
释放空间,改变指向:

//删除pos之后的节点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);

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

3.13销毁链表

在这里插入图片描述

使用循环销毁即可:

//销毁链表
void SListDestory(SLTNode** pphead)
{
	assert(pphead && *pphead);

	SLTNode* prew = *pphead;

	while (prew)
	{
		SLTNode* pcur = prew->next;
		free(prew);
		prew = pcur;
	}
	*pphead = NULL;
}
  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值