数据结构(一):链表(1:单向链表)

1.对链表的认识、预处理:

链表,别名链式存储结构或单链表,用于存储逻辑关系为 "一对一" 的数据。与s顺序表不同,链表不限制数据的物理存储状态,换句话说,使用链表存储的数据元素,其物理存储位置是随机的。

画图认知:

 通过指针来找到链表的下一个元素的位置,同时我们创建链表的时候用的也是指针,取名为头指针,头指针也是一种指针,这个时候就会出现对链表认知的一个难点,头指针指向的是这个链表头部位置,头部位置里面有也放着一个指针指向下一个元素,大体如图:

 这一点需要好好理解,区分二者的区别

为了方便后续书写,在“Slist”头文件里面进行以下预处理:

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

typedef int SLTDataType;
struct SLTNode
{
	SLTDataType Data;
	struct SLTNode* next;
};

typedef struct SLTNode SL;

2.链表的尾插:

首先我们要创建一个链表,起名为Plist,并且在初始化的时候赋值为空,方便后续头插。

显然,再进行插入的时候,需要对其是否为空进行检验,如果为空,则可以直接赋值,不然,就要一直找到链表的尾巴,这时候我们可以创建一个指针,把链表起点地址赋给它,然后一一遍历,直到它指向的next是空指针,说明现在它就是链表的尾巴,然后就可以进行操作。

void SLTPushBack(SL** PPhead, SLTDataType x)
{
	SL* newnode = CreatSLTNode(x);
	if (*PPhead == NULL)
	{
		*PPhead = newnode;
	}
	else
	{
		SL* tail = *PPhead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

关于CreatSLTNode()的解释:

由于创建新的节点用的比较多,可以直接把他封装成一个函数便于使用:

SL* CreatSLTNode(SLTDataType x)
{
	SL* newnode = (SL*)malloc(sizeof(SL));
	if (newnode == NULL)
	{
		printf("malloc failed\n");
		return;
	}
	newnode->Data = x;
	newnode->next = NULL;
	return newnode;
}

3.链表的头插:

头插与尾插相比简单不少,只需要创建一个新节点newnode,然后把newnode的下一个节点指针赋值为*PPhead,然后把头指针,也就是*PPhead改成newnode的地址即可:

void SLTPushFront(SL** PPhead, SLTDataType x)
{
	SL* newnode = CreatSLTNode(x);
	newnode->next = *PPhead;
	*PPhead = newnode;
}

4.链表的头删:

头删只需把,先把下一个结点的位置记录下来,头指针赋值成下一个节点的位置,然后free掉头指针,最后把头指针赋值成原先记录好的位置即可。

void SLTPopFront(SL** PPhead)
{
	SL* next = (*PPhead)->next;
	free(*PPhead);
	*PPhead = next;
}

5.链表的尾删:

尾删相对来说复杂一些:

1.需要考虑链表是非为空

2.需要考虑是否只有一个元素

3.怎么找打尾元素的前一个元素?

如此,我们可以用两个指针变量,一个prev,另一个tail,来采取类似快慢指针的方式去寻找

void SLTPopBack(SL** PPhead)
{
	if (*PPhead == NULL)
	{
		return;
	}
	else if ((*PPhead)->next == NULL)
	{
		free(*PPhead);
		*PPhead = NULL;
	}
	else
	{
		SL* prev = NULL;
		SL* tail = *PPhead;
		while (tail->next != NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

6.链表的查找,插入,删除指定元素:

查找就非常简单,一一遍历即可:

SL* SLTFindData(SL* Phead, SLTDataType x)
{
	SL* cur = Phead;
	while (cur != NULL)
	{
		if (cur->Data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

插入,删除需要注意的一点:如果是头、尾元素,那怎么办?再写一遍?

NO! 这样不就和头删尾删一样了吗?直接复制粘贴~

在这其中有一些细节一代略过,比如链表之间的链接之类的,都写在代码里了

void SLTInsert(SL** PPhead, SL* pos, SLTDataType x)
{
	if (pos == *PPhead)
	{
		SLTPushFront(PPhead, x);
	}
	else
	{
		SL* newnode = CreatSLTNode(x);
		SL* prev = *PPhead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

void SLTErase(SL** PPhead, SL* pos)
{
	if (pos == *PPhead)
	{
		SLTPopFront(PPhead);
	}
	else
	{
		SL* prev = *PPhead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值