链表的学习

1.链表解决的问题和概念

1.1在上一篇文章中介绍的顺序表很明显存在一些问题:①顺序表中间和头部插入数据时需要遍历数组,时间复杂度为O(N)②realloc在栈区上扩容时底层逻辑是:如果需要扩容的空间后面有足够的空间在原空间的基础上接上,如果空间不够,则需要重新开辟一块空间,再将原空间的数据复制上,再释放原空间,会有内存和时间上的消耗③顺序表在后期以两倍的大小扩容时,容易造成空间的浪费。

1.2这里我们引入链表解决问题,链表由一个个节点构成,节点可以看成是由数据和存储下一个数据的指针封装形成的数据块。当我们需要增加数据时,只需要将链表末尾节点中的指针指向新开辟的数据即可,这样链表就串联起来。下面这张图可以帮助我们理解.

19b40d84af354729b2aecf9f1d1d8665.png

 

 

1.3链表的创建:由上图我们可以用结构体构建一个链表架构(我们用int类型作为示例)

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

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType x;
	struct SListNode *next;
}SLTNode; 

2 链表功能的实现

2.1链表的尾插(尾插时需要考虑链表是否有节点)

我们可以先封装一个扩容函数,方便后续用到:

SLTNode* SLBuyNode(SLTDataType x)
{
	SLTNode* ps = (SLTNode*)malloc(sizeof(SLTNode));
	if (ps == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	ps->x = x;
	ps->next = NULL;
	return ps;
}

尾插实现函数(注意:先要判断节点存不存在。先要找到尾结点,可以用循环遍历找)

void SLPushBack(SLTNode** pphead , SLTDataType x)
{
	assert(pphead);
	if (*pphead == NULL)
	{
		*pphead = SLBuyNode(x);
	}
	else
	{
		SLTNode* pcur = *pphead;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = SLBuyNode(x);
	}
}

2.2链表的头插(注意不要忘记将头节点地址更换成新创建的节点)

9af7cff66bcb4f5bbbaa0bf545bf7f4f.png

 

void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = SLBuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;

}

2.3链表数据的打印(这里以整形为例子)

void SLPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d ", pcur->x);
		pcur = pcur->next;
	}
	printf("\n");
}

2.4链表的尾删(注意需要区分只有一个节点和多个节点两种情况)

只有一个节点时直接释放掉就完事,当多个节点时需要找到尾节点的上一个节点,将尾二节点中的next指针置为空,再将尾节点释放掉

c2bc721406124941b286aa848b412d1a.png

void SLPopBack(SLTNode** pphead)
{
	assert(pphead && *pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{	
		SLTNode* pcur = *pphead;
		SLTNode* pcurback_1 = (*pphead)->next; 
		while (pcurback_1->next)
		{
			pcurback_1 = pcurback_1->next;
			pcur = pcur->next;
		}
		pcur->next = NULL;
		free(pcurback_1);
		pcurback_1 = NULL;
	}
	
}

 

2.5链表的头删(现将头结点的地址存储在oldphead中,再将头结点置为头节点的下一个节点,再将oldphead节点即原节点释放)

2fdc1067656f4fc0a71e7dc641b05912.png

  

void SLPopFront(SLTNode** pphead)
{
	//多链条 &&单链条
	assert(pphead && *pphead);

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

 2.6节点的查找(只需遍历节点中的data数据)

SLTNode* SLFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->x == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

2.6在指定位置之前插入(先要找到pos尾前的一个位置,如果只有一个节点则可以直接调用前插函数)

1ff231549d2546918e7748e992b0b1a4.png

 

void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	SLTNode* pcur = *pphead;
	if ((*pphead)->next == NULL)
	{
		SLTNode* newnode = SLBuyNode(x);
		newnode->next = *pphead;
		*pphead = newnode;

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

2.7在指定pos位置之后插入数据(注意需要按照顺序执行)

562e909d0c8a4f28a56774cf73e2ce2f.png

 

void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

 2.8删除pos位置(需要找到pos前面的位置,如果只有一个节点直接释放掉)

bcf590d5924543acabe7d7bdc4bac05a.png

 

void SLErase(SLTNode** pphead,SLTNode* pos)
{
	assert(pphead && *pphead);
	assert(pos);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		pos = NULL;
	}
	else
	{

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

 2.9删除pos位置之后的

a35cf6196feb47b5be1fde3004e8511b.png

 

void SLEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	pos->next = pos->next->next;
	free(pos->next);
	pos->next = NULL;
}

 2.10销毁链表

void SLDestroy(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* del = *pphead;
	while (del)
	{
		SLTNode* next = del->next;
		free(del);
		del = next;
	}
	*pphead = NULL;
}

3链表的代码链接

链表的.c和.h文件可以访问我的gitee链接获取8.24/SList · 向豪/数据结构学习 - 码云 - 开源中国 (gitee.com)

谢谢!

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值