带头单向链表

概念

链表是一种物理存储结构上非连续、非顺序的存储结构,物理存储位置是随机的。数据元素的逻辑顺序是通过链表中的指针链
接次序实现的 。
在这里插入图片描述
在这里插入图片描述
物理存储位置是随机的,通过指针来表示数据之间的逻辑关系。

链表的结点

链表中每个数据的存储都由以下两部分组成:
1.数据元素本身,其所在的区域称为数据域;
2.指向直接后继元素的指针,所在的区域称为指针域;

每个结点都由数据域和指针域两部分组成

用结构体来表示结点 ,定义如下

typedef int SLTDataType;//数据类型
typedef struct SListNode
{
	SLTDataType _data;//数据域
	struct SListNode* _next;//指针域
}SListNode;

链表的结构体

typedef struct SList
{
	SListNode* _head;//头指针
}SList;

链表常用接口

一般讨论三种情况:
1.没有结点
2.需要对第一个结点经行操作
3.一般情况 (不动头结点)

void SListInit(SList* plist);//初始化链表
void SListDestory(SList* plist);//销毁链表
void SListPushFront(SList* plist, SLTDataType x);//头插
void SListPopFront(SList* plist);//头删
void SListPushBack(SList* plist);//尾插
void SListPopBack(SList* plist);//尾删

SListNode* SListFind(SList* plist, SLTDataType x);//查找
void SListInsertAfter(SListNode* pos, SLTDataType x);// 在pos的后面进行插入
void SListEraseAfter(SListNode* pos, SLTDataType x);// 在pos的前面进行插入

void SListRemove(SList* plist, SLTDataType x);//删除元素第一个元素x
void SListPrint(SList* plist);//打印链表
void SlistReverse(SList*plist);//翻转链表

初始化和销毁链表

传参为链表的地址
plist是指针 要访问_head ,使用plist->_head;
plist->_head 表示第一个结点 ,plist->_head ->_next 表示第二个结点。当plist->_head == NULL时 ,表示链表中没有结点

void SListInit(SList* plist)
{
	assert(plist != NULL);
	plist->_head = NULL;
}
void SListDestory(SList* plist)
{
	assert(plist != NULL);
	SListNode*next = NULL;
	for (SListNode*cur = plist->_head; cur!=NULL;cur= next)
	{
		next = cur->_next;
		free(cur);
	}
	plist->_head = NULL;
}

插入和删除

注意:头插法相对尾插简单,使用尾插时,要找最后一个结点,使用尾删时,要找倒数第二个结点。尾删时考虑:1.没有结点 2.一个结点 3.一般情况

//头插和头删
void SListPushFront(SList* plist, SLTDataType x)//头插
{
	assert(plist != NULL);
	SListNode*Node = (SListNode*)malloc(sizeof(SListNode));//产生新结点
	Node->_data = x;
	Node->_next = plist->_head;
	plist->_head = Node;
}
void SListPopFront(SList* plist)//头删
{
	assert(plist != NULL);
	if (plist->_head == NULL)
		return;
	SListNode*cur = plist->_head->_next;
	free(plist->_head);
	plist->_head = cur;
}

//尾插和尾删
void SListPushBack(SList* plist, SLTDataType x)//尾插
{
	assert(plist != NULL);
	SListNode*cur = plist->_head;
	SListNode*Node = (SListNode*)malloc(sizeof(SListNode));
	assert(Node);
	Node->_data = x;
	Node->_next = NULL;
	if (plist->_head == NULL)//没有结点的情况
	{
		plist->_head = Node;
		return;
	}
	while (cur->_next != NULL)
	{
		cur = cur->_next;
	}
	cur->_next = Node;//cur表示最后一个结点
}
void SListPopBack(SList* plist)//尾删
{
	assert(plist);
	if (plist->_head == NULL)
		return;
	if (plist->_head->_next == NULL)
	{
		free(plist->_head);
		plist->_head = NULL;
	}
	SListNode*cur = plist->_head;
	while (cur->_next->_next != NULL)
	{
		cur = cur->_next;
	}
	free(cur->_next);
	cur->_next = NULL;
}

查 增 删

在pos结点后增加结点 :让新结点指向 pos指向的下一个结点 ,再让pos指向新结点
在pos前增加结点:与在pos后增加结点一样 ,但是要把pos的数据改为新数据,pos后的新结点数据改为pos原数据
TIP: 知道某一结点 ,只能在这一个结点后面增加结点,但是结点存储的数据是可以修改的,从而实现在pos前增加结点。

SListNode* SListFind(SList* plist, SLTDataType x)//查找
{
	assert(plist);
	SListNode*cur = plist->_head;
	for (cur; cur != NULL; cur = cur->_next)
	{
		if (cur->_data == x)
			return cur;
	}
	return NULL;
}

void SListInsertAfter(SListNode* pos, SLTDataType x)//在pos后面增加结点  数据为x
{
	assert(pos);
	SListNode*node = (SListNode*)malloc(sizeof(SListNode));
	assert(node);
	node->_data = x;
	node->_next = pos->_next;
	pos->_next = node;
}
void SListRemove(SList* plist, SLTDataType x)//删除第一个元素x
{
	assert(plist);
	SListNode *cur = plist->_head;
	if (plist->_head == NULL)
		return;
	if (cur->_data == x)
	{
		plist->_head = cur->_next;
		free(cur);
		return;
	}
	while (cur->_next!= NULL)
	{
		if (cur->_next->_data == x)
		{
			SListNode*ptr = cur->_next;
			cur->_next = cur->_next->_next;
			free(ptr);
			return;
		}
		cur = cur->_next;
	}
}

翻转单链表

用三个指针来实现单链表的翻转
p1 表示翻转后 链表的最后一个结点
p2 表示当前结点
p3 表示下一个结点

以下图为例:

翻转前的链表
在这里插入图片描述在这里插入图片描述
翻转后的链表
在这里插入图片描述

结合代码操作过程如下:

刚开始时 p1指向空 p2指向第一个结点 p3指向第二个结点
在这里插入图片描述
下一步 :p2->_next = p1;让第一个结点指向p1 ,此时p1为空 (注意第一个结点翻转后为最后一个结点 ,翻转后此结点下一个指向空。 p1 = p2 ; p1 表示翻转后 链表的最后一个结点,p2 = p3;让p2指向下一个结点;p3 = p3->_next;让p3指向下一个结点。

在这里插入图片描述
经过一次操作后 如上图所示;
p1 指向 1结点 1结点指向空 p2指向2结点 p3指向3结点
再经过一次操作后如下图所示:
在这里插入图片描述
。。。。
最终翻转后 如下:
在这里插入图片描述
最后 再让头结点指向p1 实现翻转。
注意: 循环的终止条件是p2指向NULL,表示所有结点都完成翻转。移动p3时要对其进行判断,防止对空指针进行移动

void SlistReverse(SList*plist)//三个指针的方式来翻转链表
{
	assert(plist);
	if (plist->_head == NULL)
		return;
	SListNode*p1 = NULL;
	SListNode*p2 = plist->_head;
	SListNode*p3 = plist->_head->_next;
	while (p2 != NULL)
	{
		p2->_next = p1;
		p1 = p2;
		p2 = p3;
		if (p3 != NULL)//这里是p3 而不是p3->_next
		{
			p3 = p3->_next;
		}
	}
	plist->_head = p1;//让头指针指向第一个结点
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值