【数据结构(C语言描述)】线性表之无头单向不循环链表


    1.1 链表的概念及结构

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

在这里插入图片描述

  • 分类:带头/不带头,单向/双向,循环/不循环,排列组合后一共有8种不同的链表。
    在这里插入图片描述

🔵特点
    ① 逻辑上连续,但是物理上可能连续可能不连续。
    ② 创建的空间一般在堆上。(记得free

    1.2 接口实现

// 无头+单向+非循环链表
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;//数据域
	struct SListNode* next;//指针域 //结构体自引用
}SListNode;

       1.动态申请一个节点

为了避免后续创建结点操作的冗余,我们可以编写一个申请结点的函数。

//创建一个新结点,返回新结点地址
SListNode* BuySLTNode(SLTDataType x)
{
	SListNode* node = (SListNode*)malloc(sizeof(SListNode));//向新结点申请空间
	if (node == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	node->data = x;//将数据赋值到新结点的数据域
	node->next = NULL;//将新结点的指针域置空
	return node;//返回新结点地址
}

       2.打印

//打印链表
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;//接收头指针
	while (cur != NULL)//判断链表是否打印完毕
	{
		printf("%d->", cur->data);//打印数据
		cur = cur->next;//指针指向下一个结点
	}
	printf("NULL\n");//打印NULL,表明链表最后一个结点指向NULL
}

       3.头插

在这里插入图片描述

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySLTNode(x);//创建一个新的结点
	newnode->next = *pplist;//新结点的指针域指向下一个结点
	*pplist = newnode;//重新设置头结点
}

       4.尾插

在这里插入图片描述

void SListPushBack(SListNode** pplist, SLTDataType x)
{
	SListNode* newnode = BuySLTNode(x);//创建一个新结点
	if (*pplist == NULL)//如果链表为空
	{
		*pplist = newnode;//直接把新节点给头指针
	}
	else
	{
		SListNode* tail = *pplist;//临时创建tail目的是寻找尾部
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

       5.头删

void SListPopFront(SListNode** pplist)
{
	if (*pplist == NULL)
	{
		return;
		//assert(*pplist!=NULL)
	}
	else
	{
		SListNode* temp = *pplist;
		*pplist = (*pplist)->next;
		free(temp);
		temp = NULL;
	}
}

       6.尾删

在这里插入图片描述

//尾删
void SListPopBack(SListNode** pplist)
{
	SListNode* prev = NULL;
	if (*pplist == NULL)//空
	{
		return;
		//assert(*pplist!=NULL);
	}
	else if((*pplist)->next==NULL)//只有一个结点
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;//free后记得置空
		prev->next = NULL;
	}
}

在这里插入图片描述

void SListPopBack(SListNode** pplist)
{
	if (*pplist == NULL)//空
	{
		return;
		//assert(*pplist!=NULL);
	}
	else if((*pplist)->next==NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next= NULL;
	}
}

       7.某一个位置前/后插

//在给定位置之后插入
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);//确保传入地址不为空
	SListNode* newnode = BuySLTNode(x);//申请一个新结点
	newnode->next = pos->next;//让新结点的指针域指向地址为pos的结点的下一个结点
	pos->next = newnode;//让地址为pos的结点指向新结点
}
//在给定位置之前插入
void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDataType x)
{
	assert(pos);//确保传入地址不为空
	SListNode* newnode = BuySLTNode(x);//申请一个新结点
	if (pos == *pplist)//判断给定位置是否为头指针指向的位置
	{
		newnode->next = pos;//让新结点的指针域指向地址为pos的结点
		*pplist = newnode;//让头指针指向新结点
	}
	else
	{
		SListNode* prev = *pplist;//接收头指针
		while (prev->next != pos)//找到地址为pos的结点的前一个结点
		{
			prev = prev->next;
		}
		newnode->next = prev->next;//让新结点的指针域指向地址为pos的结点
		prev->next = newnode;//让前一个结点指向新结点
	}
}

       8.某一个位置前/后删

//删除给定位置之后的值
void SListErasetAfter(SListNode* pos)
{
	assert(pos);//确保传入地址不为空
	if (pos->next == NULL)//判断传入地址是否为最后一个结点的地址
	{
		return;
	}
	SListNode* after = pos->next;//待删除的结点
	pos->next = after->next;//让pos结点指向待删除的结点的下一个结点
	free(after);//释放结点
	after = NULL;//及时置空
}
//删除给定位置的值
void SListErasetCur(SListNode** pplist, SListNode* pos)
{
	assert(pos);//确保传入地址不为空
	if (pos == *pplist)//判断待删除的结点是否为第一个结点
	{
		*pplist = pos->next;//让头指针指向第二个结点
		free(pos);//释放第一个结点
		pos=NULL;//及时置空
	}
	else
	{
		SListNode* prev = *pplist;//接收头指针
		while (prev->next != pos)//找到待删除结点的前一个结点
		{
			prev = prev->next;
		}
		prev->next = pos->next;//让待删除的结点的前一个结点指向待删除结点的后一个结点
		free(pos);//释放待删除结点
		pos = NULL;//及时置空
	}
}

       9.查找数据

SListNode* SListFind(SListNode* plist, SLTDataType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (x == cur->data)
			return cur;
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

[ 拓展 ] 多个数值查找并统计:

	SListNode* pos = SListFind(plist, 2);
	int i = 0;
	while (pos)
	{
		pos = SListFind(plist, 2);
		i += 1;
	}

       10.修改数据

查找后修改数据

void SListModify(SListNode* pos, SLTDataType x)
{
	pos->data = x;
}

       11.删除整个链表

void SListDestory(SLTNode** pphead)
{
	assert(*pphead);//判断头是否为空
	SLTNode* cur = *pphead;//将头结点的地址保存在cur
	while (cur)
	{
		SLTNode* next = cur->next;//next指向待删除结点的下一个结点
		free(cur);//删除结点
		cur = next;//指针指向删除完毕后的下一个结点
	}
	*pphead = NULL;//置空
}

    1.3 顺序表和链表的区别

不同点顺序表链表
存储空间上连续逻辑连续,物理不一定连续
随机访问
任意位置插入或者删除元素时间复杂度: O ( N ) O(N) O(N)改变指针指向
应用场景元素高效存储+频繁访问任意结点插入/删除
缓存利用率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XiYang-DING

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值