线性表的链式存储结构——链表

这篇具有很好参考价值的文章主要介绍了线性表的链式存储结构——链表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、顺序表优缺点

二、链表的定义

①:概念:

②:结点

③:单链表的定义:

④:头结点:

三 、单链表的C语言结构定义:

四、单链表基本操作:

四.1、遍历单链表

四.2、用malloc函数创建新结点

四.3、尾插法

四.4、头插法

四.5、尾删法

四.6、头删法


一、顺序表优缺点

优点:我们知道顺序表结构简单,便于随机访问表中任一元素;

缺点:顺序存储结构不利于插入和删除,不利于扩充,也容易造成空间浪费。

二、链表的定义

①:概念:

用一组任一的存储单元存储线性表的数据元素(可以是连续,也可以是不可连续的),数据元素之间的逻辑关系借助指示元素存储位置的指针来表示,这种存储方式叫做线性表的 链式存储结构 ,简称 链表

②:结点

为了表示数据元素间的逻辑关系,除了存储数据本身信息之外,还需要存放其直接后继的存储位置(地址),这两部分(数据域和指针域)组成一个 结点 用于表示一个元素。
数据域:存储数据元素的信息;
指针域:存放直接后继的元素的地址。
表示图如下:
线性表的链式存储结构——链表,数据结构与算法,链表,数据结构
同时还需要注意以下几点:
注意:我们规定链表的尾结点(最后一个结点)的指针域为NULL,这样可以方便进行操作。
线性表的链式存储结构——链表,数据结构与算法,链表,数据结构

③:单链表的定义:

若链表的每个结点只包含一个指针域,则称此链表为 线性链表单链表

④:头结点:

有时为了操作方便,在单链表的第一个结点之前添加一个结点,称为 头结点或伪结点
不带哨兵的头结点就直接使用;
线性表的链式存储结构——链表,数据结构与算法,链表,数据结构
带哨兵的头结点就按以下规则:
头结点的数据域可以不存放任何信息,也可以存放其他特殊信息;
头结点的指针域存放第一个结点的存储地址,即指向第一个结点;
线性表的链式存储结构——链表,数据结构与算法,链表,数据结构
本次小编用的是不带哨兵的头结点。
有头结点的单链表叫做“带头结点的单链表”。

三 、单链表的C语言结构定义:

typedef int SLDataType;//方便以后跟改数据类型

typedef struct SLisrNode
{
	SLDataType data;//数据域
	struct SListNode* next;//指针域
}Lnode, * LinkList;//用typedef重定义后,Londe为结点类型,LinkList为指向结点的指针类型

四、单链表基本操作:

作几点说明

①:为了方便让大家感受一下单链表,所以先实现遍历单链表,让大家体会其结构的韵味;

②:我们一般都默认用带有头结点的单链表;

③:大家一般都会写与控制台有互动的代码,但小编这里只是带大家入门上手,只是简单实现,所以大家可以根据小编的思路自行设计。

④:本次实现小编也是采用多文件操作,没听说过的小伙伴可以大致了解一下,就几句话意思很简单。

⑤:跟往常不一样的是,小编会把详细说明放在注释里面,大家请认真解读,有疑问或没看懂的欢迎大家评论区讨论。

四.1、遍历单链表

源代码:

//遍历单链表
void SLTPrint(SLTNode* phead)
{
	//一般头指针phead我们都不会动,方便我们多次操作
	//所以我们可以创建一个临时指针来进行操作
	SLTNode* tmp = phead;
	//因为单链表尾结点的指针域为NULL,所以循环条件可以为tmp
	//当tmp为NULL时,说明链表遍历完成,并且退出循环
	while (tmp)
	{
		//打印数据域(打印"->"只是方便展示链表结构)
		printf("%d->", tmp->data);
		//因为指针域指向下一个结点,所以可以通过赋值方法找到下一个结点
		tmp = tmp->next;
	}
	//方便展示链表结构,所以末尾打印一个NULL
	printf("->NULL\n");
}

四.2、用malloc函数创建新结点

因为指针phead刚开始创建时并没有指向任何结点,需要在程序执行过程中通过按结点的类型向系统申请建立一个新结点,通过调用标准函数malloc动态开辟空间生成,

创建一个新结点,具体格式如下:

phead=(SLTNnode*)malloc(sizeof(SLTNode));

当phead结点不需要时,我们应该用free函数释放空间,收回结点;

因为有多种操作,所以我们将这个操作写成一个函数,方便后续调用;

源代码:

//创建新结点
//因为要返回动态申请的结点,所以有返回类型
SLTNode* BuySLiseNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	//判断是否开辟成功
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	//填充数据
	newnode->data = x;
	newnode->next = NULL;
	//返回
	return newnode;
}

四.3、尾插法

尾插法:顾名思义就是在链表尾部进行插入数据;

大致步骤分为两步:

①:找到链表尾结点(根据尾结点的next域为NULL来作为循环条件进行寻找);

②:将新结点链接到尾结点的next域即可;

源代码即解释如下:

//尾插法
//函数第一个形参是二级指针,是存储链表的地址,用的传址调用
//函数第二个形参是要插入的数据
void SLTPushBack(SLTNode** pphead1, SLTDataType x)
{
	//得到一个新结点
	SLTNode* newnode = BuySLiseNode(x);
	//若链表为空链表,则直接将新结点赋值给链表,因为我们是传址调用,可以改变外面链表的内容
	if (*pphead1 == NULL)
	{
		*pphead1 = newnode;
	}
	else
	{
		//同理将链表拷贝到一个临时结点中便于操作
		SLTNode* tmp = *pphead1;
		//①:找到尾结点
		//因为尾结点的next域为NULL,所以可以作为循环判断条件
		while (tmp->next == NULL)
		{
			tmp = tmp->next;
		}
		//②:链接
		tmp->next = newnode;
		//这里可能有的小伙伴不理解,"觉得这里不也是直接赋值吗,怎么改变外部链表的"?
		//所以大家就要注意一句话,如下:
		//1.改变结构体,用结构体指针
		//2.改变结构体指针,要用结构体指针的指针(二级指针)
		// 
		//上面的pphead就是一个结构体二级指针,用于改变结构体指针
		//但这里的next是结构体成员,所以我们只需要改变结构体,所以就用结构体指针
		//而tmp本来就是我们结构头指针拷贝过来的,所以改变了tmp的next就相当于改变了外部链表的尾结点的next
	}

	//因为tmp、newnode这些都是局部变量,函数结束后会自动销毁
	//第二次又会从初始位置开始操作,所以我们不用调整这些变量的值
	//但链表结点内容不会被销毁,因为我们是用malloc函数在堆区上面申请的空间,只有free后才会销毁
}

测试结果:

void TestList2()
{
	SLTNode* phead = NULL;//头结点,用于链接新结点
	//尾插100、200、300
	SLTPushBack(&phead, 100);
	SLTPushBack(&phead, 200);
	SLTPushBack(&phead, 300);
	//打印
	SLTPrint(phead);

}

int main()
{
	//测试
//	TestSLTist1();
	TestList2();
	return 0;
}

运行结果:

线性表的链式存储结构——链表,数据结构与算法,链表,数据结构

四.4、头插法

当我们理解清楚尾插法里面的各种细节后,头插以及后面的操作都会觉得很简单;

头插大致分为

①:将新结点的next域指向首结点;

②:再让头指针指向新结点;

源代码及解释如下:


//头插法
//形参和尾插法是相同的道理
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	//因为我们每一步都要使用不带哨兵的头结点,所以每次都要改变结构体指针
	// 所以形参用二级指针是必须的,并且不用区分链表是否为空
	//先得到一个新结点
	SLTNode* tmp = BuySLiseNode(x);
	//①:新结点的next域指向首结点
	tmp->next = *pphead;
	//②:头结点指向新结点
	*pphead = tmp;
}

四.5、尾删法

尾删法也涉及到许多细节,大家也需要仔细思考;

因为我们使用的是不带哨兵的头指针,就会涉及改变结构体和改变结构体指针的问题;

只要记住:

改变next和data域就是改变结构体,只需要使用结构体指针;

改变头指针就是改变结构体指针,这时需要结构体二级指针;

尾删法分为三中=种情况:

①:链表为空;

②:链表里面只有一个结点;

③:链表里面有两个或两个以上的结点;

具体细节看注释:
源代码如下:


//头删法
//形参是结构体二级指针,因为分为几种情况,有一种情况会改变结构体指针
void SLTPopBack(SLTNode** pphead)
{
	//情况一:链表为空则提示删除失败
	if (*pphead == NULL)
	{
		printf("此链表为空,无法删除!\n");
		return;
	}
	//情况二:只有一个结点,此时需要使用头指针,所以要用到二级指针
	//直接用free释放掉头指针即可,因为我们用的是不带哨兵的头指针,头指针指向的就是首元素
	//又因为只有一个元素,所以直接释放头指针,在将头指针置空即可
	//
	if((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//情况三:有两个及其以上的结点:步骤如下:
		//①:定位到尾结点;
		//②:释放尾结点;
		//将尾结点的前一个结点的next域置空(所以一定要保存前一结点的地址)

	//同理先将链表内容拷贝到临时指针中进行操作,因为我们操作的是结构体,所以用结构体指针即可实现
		SLTNode* tmp = *pphead;
		//如果tmp->next->next为NULL,说明tmp->next就是尾结点,tmp就是尾结点的前一个结点,所以就可以进行释放了
		while (tmp->next->next)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}

四.6、头删法

头删法比较简单,但因为是在头部操作,所以每一步都是在改变结构体指针(即头指针),所以每一步都要用结构体二级指针,所以形参是结构体二级指针;

头删法只有两种情况:

①:链表为空,与尾删一样;

②:链表不为空;这里没有区分一个结点和多个结点是因为我们每一步都要用到结构体二级指针,所以两者操作是一样的;

具体细节即源代码如下:

//头删法
//参数同尾删一样,会改变结构体指针,所以用结构体二级指针
void SLTPopFront(SLTNode** pphead)
{
	//情况一:链表为空,同尾删一样
	if (*pphead == NULL)
	{
		printf("此链表为空,无法删除!\n");
		return;
	}

	//情况二:链表不为空
	//①:先将第二个结点的地址(即(*pphead->next))保存到临时指针
	//②:再释放头指针(即释放首元素)
	//③:再将存放第二个结点地址的临时指针赋给头指针*pphead
	SLTNode* nextnode = (*pphead)->next;
	free(*pphead);
	*pphead = nextnode;
}

四.7、查找指定结点

这个操作很简单,只需遍历链表即可;

//寻找结点
//参数一用了结构体指针,因为不需要改变结构体指针,只需要遍历链表,所以不需要结构体二级指针
//参数二是我们要找的数据x
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	//同理习惯用一个临时指针来进行操作
	SLTNode* tmp = phead;
	//用循环遍历链表,同时比较数据域是否等于x,若相等则返回tmp结点,若 没找到。则返回空
	while (tmp)
	{
		if (tmp->data == x)
		{
			return tmp;
		}
		tmp = tmp->next;
	}
	//没找到返回NULL
	return NULL;
}

四.8、在pos结点之前插入结点

有三种情况:

情况一:链表为空;

情况二:链表中只有一个结点;

情况三:链表中有多个结点。

具体解释看注释:


//在pos结点之前插入x
//参数一是结构体二级指针
//参数二是pos位置
//参数三是要插入的数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	//情况一、链表为空表
	if (*pphead == NULL)
	{
		printf("此链表为空,插入失败!\n");
		return;
	}
	//情况二、链表中只有一个结点
	if (*pphead == pos)
	{
		//即为头插,直接调用头插函数,所以会改变头指针,所以要用结构体二级指针
		SLTPushFront(pphead, x);
	}
	//情况二、链表中有多个结点
	else
	{
		//临时指针
		SLTNode* tmp = *pphead;
    //找到pos结点的前一个结点
		while (tmp->next == pos)
		{
			//得到一个新结点
			SLTNode* newnode = BuySLiseNode(x);
			//开始插入
			newnode->next = pos;
			tmp->next = newnode;
		}
	}
}

四.8、在pos结点之后插入结点

这种情况就比四.7简单多了,因为我们是在结点的后面插入,所以不涉及头插(即不用结构体二级指针),也不用循环找什么结点,直接插即可;

情况一、链表为空;

情况二、链表不为空,因为我们是在结点之后插入,所以不涉及改变头指针(不涉及头插)。所以用结构体指针就能达到目的;

如下:

//在pos结点之后插入新结点
void SLTInsertAfter(SLTNode* phead, SLTNode* pos, SLTDataType x)
{
	//情况一、链表为空表
	if (phead == NULL)
	{
		printf("此链表为空,插入失败!\n");
		return;
	}
	//情况二、链表不为空
	//得到一个新结点
	SLTNode* newnode = BuySLiseNode(x);
	//开始插入
	newnode->next = pos->next;
	pos->next = newnode;
}

四.9、删除pos位置的结点

有三种情况:

情况一、链表为空;

情况二、链表只有一个结点(头删法);

情况三、链表有多个结点;

如下:
 

//删除pos位置的结点
void SLTErace(SLTNode** pphead, SLTNode* pos)
{
	//情况一、链表为空表
	if (*pphead == NULL)
	{
		printf("此链表为空,删除失败!\n");
		return;
	}
	//情况二、链表中只有一个结点(头删)
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	//情况三、链表中有多个结点
	else
	{
		//临时指针
		SLTNode* tmp = *pphead;
		//找到指针结点的前一个结点
		while (tmp->next != pos)
		{
			tmp = tmp->next;
		}
		//开始删除
		tmp->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

五、测试源代码

main.c:

#include"Slist.h"

测试函数
//void TestSLTist1()
//{
//	int n = 0;
//	printf("请输入链表长度;>\n");
//	scanf("%d", &n);
//	printf("请输入每个结点的值");
//	SLTNode* phead = NULL;//头结点,用于链接新结点
//	int i = 0;
//	for (i = 0; i < n; i++)
//	{
//		SLTDataType val;
//		scanf("%d", &val);
//		//获取新结点
//		SLTNode* newnode = BuySLiseNode(val);
//		//一个个结点有了,我们还需要将其串起来
//		//即链接新结点
//		if (phead == NULL)
//		{
//			phead = newnode;
//		}
//		else
//		{
//			//头插(这种方法不用考虑刚开始链表是否为空)
//			newnode->next = phead;
//			phead = newnode;
//		}
//	}
//	//打印链表
//	SLTPrint(phead);
//
//}

void TestList2()
{
	SLTNode* phead = NULL;//头结点,用于链接新结点

	//尾插100、200、300
	SLTPushBack(&phead, 100);
	SLTPushBack(&phead, 200);
	SLTPushBack(&phead, 300);
	//打印
	SLTPrint(phead);

	//头插10/20/30
	SLTPushFront(&phead, 10);
	SLTPushFront(&phead, 20);
	SLTPushFront(&phead, 30);
	//打印
	SLTPrint(phead);

	//尾删两个元素;
	SLTPopBack(&phead);
	SLTPopBack(&phead);
	//打印
	SLTPrint(phead);

	//头删两个元素
	SLTPopFront(&phead);
	SLTPopFront(&phead);
	//打印
	SLTPrint(phead);

	// 100之前插入300
	SLTNode* pos = SLTFind(phead,100);
	SLTInsert(&phead, pos, 300);
	//100之后插入1000
	SLTInsertAfter(phead, pos, 1000);
	//打印
	SLTPrint(phead);

	//删除300和1000位置的结点
	SLTNode* pos1 = SLTFind(phead, 300);
	SLTNode* pos2 = SLTFind(phead, 1000);
	SLTErace(&phead, pos1);
	SLTErace(&phead, pos2);
	//打印
	SLTPrint(phead);
}

int main()
{
	//测试
//	TestSLTist1();
	TestList2();
	return 0;
}

SList.c:

#include"Slist.h"

//创建新结点
//因为要返回动态申请的结点,所以有返回类型
SLTNode* BuySLiseNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	//判断是否开辟成功
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	//填充数据
	newnode->data = x;
	newnode->next = NULL;
	//返回
	return newnode;
}

//遍历单链表
void SLTPrint(SLTNode* phead)
{
	//一般头指针phead我们都不会动,方便我们多操作
	//所以我们可以创建一个临时指针来进行操作
	SLTNode* tmp = phead;
	//因为单链表尾结点的指针域为NULL,所以循环条件可以为tmp
	//当tmp为NULL时,说明链表遍历完成,并且退出循环
	while (tmp)
	{
		//打印数据域(打印"->"只是方便展示链表结构)
		printf("%d->", tmp->data);
		//因为指针域指向下一个结点,所以可以通过赋值方法找到下一个结点
		tmp = tmp->next;
	}
	//方便展示链表结构,所以末尾打印一个NULL
	printf("NULL\n");
}

//尾插法
//函数第一个形参是二级指针,是存储链表的地址,用的传址调用
//函数第二个形参是要插入的数据
void SLTPushBack(SLTNode** pphead1, SLTDataType x)
{
	//得到一个新结点
	SLTNode* newnode = BuySLiseNode(x);
	//若链表为空链表,则直接将新结点赋值给链表,因为我们是传址调用,可以改变外面链表的内容
	if (*pphead1 == NULL)
	{
		*pphead1 = newnode;
	}
	else
	{
		//同理将链表拷贝到一个临时结点中便于操作
		SLTNode* tmp = *pphead1;
		//①:找到尾结点
		//因为尾结点的next域为NULL,所以可以作为循环判断条件
		while (tmp->next != NULL)
		{
			tmp = tmp->next;
		}
		//②:链接
		tmp->next = newnode;
		//这里可能有的小伙伴不理解,"觉得这里不也是直接赋值吗,怎么改变外部链表的"?
		//所以大家就要注意一句话,如下:
		//1.改变结构体,用结构体指针
		//2.改变结构体指针,要用结构体指针的指针(二级指针)
		// 
		//上面的pphead就是一个结构体二级指针,用于改变结构体指针
		//但这里的next是结构体成员,所以我们只需要改变结构体,所以就用结构体指针
		//而tmp本来就是我们结构头指针拷贝过来的,所以改变了tmp的next就相当于改变了外部链表的尾结点的next
	}

	//因为tmp、newnode这些都是局部变量,函数结束后会自动销毁
	//第二次又会从初始位置开始操作,所以我们不用调整这些变量的值
	//但链表结点内容不会被销毁,因为我们是用malloc函数在堆区上面申请的空间,只有free后才会销毁
}

//头插法
//形参和尾插法是相同的道理
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	//因为我们每一步都要使用不带哨兵的头结点,所以每次都要改变结构体指针
	// 所以形参用二级指针是必须的,并且不用区分链表是否为空
	//先得到一个新结点
	SLTNode* tmp = BuySLiseNode(x);
	//①:新结点的next域指向首结点
	tmp->next = *pphead;
	//②:头结点指向新结点
	*pphead = tmp;
}

//头删法
//形参是结构体二级指针,因为分为几种情况,有一种情况会改变结构体指针
void SLTPopBack(SLTNode** pphead)
{
	//情况一:链表为空则提示删除失败
	if (*pphead == NULL)
	{
		printf("此链表为空,无法删除!\n");
		return;
	}
	//情况二:只有一个结点,此时需要使用头指针,所以要用到二级指针
	//直接用free释放掉头指针即可,因为我们用的是不带哨兵的头指针,头指针指向的就是首元素
	//又因为只有一个元素,所以直接释放头指针,在将头指针置空即可
	//
	if((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//情况三:有两个及其以上的结点:步骤如下:
		//①:定位到尾结点;
		//②:释放尾结点;
		//将尾结点的前一个结点的next域置空(所以一定要保存前一结点的地址)

	//同理先将链表内容拷贝到临时指针中进行操作,因为我们操作的是结构体,所以用结构体指针即可实现
		SLTNode* tmp = *pphead;
		//如果tmp->next->next为NULL,说明tmp->next就是尾结点,tmp就是尾结点的前一个结点,所以就可以进行释放了
		while (tmp->next->next)
		{
			tmp = tmp->next;
		}
		free(tmp->next);
		tmp->next = NULL;
	}
}

//头删法
//参数同尾删一样,会改变结构体指针,所以用结构体二级指针
void SLTPopFront(SLTNode** pphead)
{
	//情况一:链表为空,同尾删一样
	if (*pphead == NULL)
	{
		printf("此链表为空,无法删除!\n");
		return;
	}

	//情况二:链表不为空
	//①:先将第二个结点的地址(即(*pphead->next))保存到临时指针
	//②:再释放头指针(即释放首元素)
	//③:再将存放第二个结点地址的临时指针赋给头指针*pphead
	SLTNode* nextnode = (*pphead)->next;
	free(*pphead);
	*pphead = nextnode;
}


//寻找结点
//参数一用了结构体指针,因为不需要改变结构体指针,只需要遍历链表,所以不需要结构体二级指针
//参数二是我们要找的数据x
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	//同理习惯用一个临时指针来进行操作
	SLTNode* tmp = phead;
	//用循环遍历链表,同时比较数据域是否等于x,若相等则返回tmp结点,若 没找到。则返回空
	while (tmp)
	{
		if (tmp->data == x)
		{
			return tmp;
		}
		tmp = tmp->next;
	}
	//没找到返回NULL
	return NULL;
}

//在pos结点之前插入x
//参数一是结构体二级指针
//参数二是pos位置
//参数三是要插入的数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	//情况一、链表为空表
	if (*pphead == NULL)
	{
		printf("此链表为空,插入失败!\n");
		return;
	}
	//情况二、链表中只有一个结点
	if (*pphead == pos)
	{
		//即为头插,直接调用头插函数,所以会改变头指针,所以要用结构体二级指针
		SLTPushFront(pphead, x);
	}
	//情况二、链表中有多个结点
	else
	{
		//临时指针
		SLTNode* tmp = *pphead;
		while (tmp->next == pos)
		{
			//得到一个新结点
			SLTNode* newnode = BuySLiseNode(x);
			//开始插入
			newnode->next = pos;
			tmp->next = newnode;
		}
	}
}

//在pos结点之后插入新结点
void SLTInsertAfter(SLTNode* phead, SLTNode* pos, SLTDataType x)
{
	//情况一、链表为空表
	if (phead == NULL)
	{
		printf("此链表为空,插入失败!\n");
		return;
	}
	//情况二、链表不为空
	//得到一个新结点
	SLTNode* newnode = BuySLiseNode(x);
	//直接开始插入
	newnode->next = pos->next;
	pos->next = newnode;
}

//删除pos位置的结点
void SLTErace(SLTNode** pphead, SLTNode* pos)
{
	//情况一、链表为空表
	if (*pphead == NULL)
	{
		printf("此链表为空,删除失败!\n");
		return;
	}
	//情况二、链表中只有一个结点(头删)
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	//情况三、链表中有多个结点
	else
	{
		//临时指针
		SLTNode* tmp = *pphead;
		//找到指针结点的前一个结点
		while (tmp->next != pos)
		{
			tmp = tmp->next;
		}
		//开始删除
		tmp->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

SList.h:
 

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include<stdlib.h>
#include<string.h>

typedef int SLTDataType;//方便以后跟改数据类型

typedef struct SListNode
{
	SLTDataType data;//数据域
	struct SListNode* next;//指针域
}SLTNode, * LinkList;//用typedef重定义后,Londe为结点类型,LinkList为指向结点的指针类型


//遍历单链表
void SLTPrint(SLTNode* phead);

//创建新结点
SLTNode* BuySLiseNode(SLTDataType x);

//尾插法
void SLTPushBack(SLTNode** pphead1, SLTDataType x);

//头插法
void SLTPushFront(SLTNode** pphead, SLTDataType x);

//尾删法
void SLTPopBack(SLTNode** pphead);

//头删法
void SLTPopFront(SLTNode** pphead);

//寻找结点
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在pos结点之前插入新结点
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在pos结点之后插入新结点
void SLTInsertAfter(SLTNode* phead, SLTNode* pos, SLTDataType x);

//删除pos位置的结点
void SLTErace(SLTNode** pphead, SLTNode* pos);

本次知识到此结束,希望对你有所帮助!文章来源地址https://www.toymoban.com/news/detail-718116.html

到了这里,关于线性表的链式存储结构——链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构(4) 链表(链式存储)

    顺序表优点:可随机存取,存储密度高,缺点:要求大片连续空间,改变容量不方便。 单链表优点:不要求大片连续空间,改变容量方便,缺点:不可随机存取,要耗费一定空间存放指针。 定义单链表的代码: 定义数据领和指针域 定义一个新节点 定义typedef来缩短函数书写

    2024年02月22日
    浏览(43)
  • 【玩转408数据结构】线性表——单链表的定义以及增删改查(线性表的链式表示 上)

            到这里我们已经了解到线性表是具有 相同数据类型 的 有限个数据元素 序列,而线性表的顺序存储也就是顺序表,顺序表的存储形式十分直观,我们在实现时使用数组进行实现,但顺序表在插入或者删除元素时需要移动大量元素,那么怎么样才能在插入删除元素时不

    2024年02月21日
    浏览(57)
  • 数据结构---链式存储的线性表

    指针    定义:       指针也就是内存地址 ,指针变量是 用来存放内存地址 的 变量 ,在同一CPU构架下,不同类型的指针变量所占用的存储单元长度是相同的,而 存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。 有了指针以后,可以对数据本身,也可以

    2024年02月15日
    浏览(40)
  • 【考研复习】24王道数据结构课后习题代码|2.3线性表的链式表示

    删除结点:1、2、4 就地逆置:5、 合并链表 分解链表:10、11、 去除重复元素:12、 并集:14、15 循环链表:17、18、19、20 头插法、尾插法重点基础必掌握。 判断是否有环:21 用函数递归调用删除结点。 注意删除结点的时候可能断链。 利用函数调用的特性反向输出。 设置保

    2024年02月13日
    浏览(47)
  • 【数据结构】线性表(顺序存储和链式存储)两种方法,细节满满,保你学会

    ⭐⭐⭐⭐⭐⭐ 🎊专栏【数据结构】 🍔喜欢的诗句:更喜岷山千里雪 三军过后尽开颜。 🎆音乐分享【勋章】 大一同学小吉,欢迎并且感谢大家指出我的问题🥰 ⭐⭐⭐⭐⭐⭐  目录 ⭐定义:  ⭐ 理解: ⭐存储方式 : ⭐顺序存储的优缺点: 优点: 缺点: ⭐链式存储的优

    2023年04月09日
    浏览(39)
  • 数据结构(王道)——线性表的存储结构之循环表

      创建并初始化、判断循环单链表是否为空、判断结点p是否为循环单链表的表尾结点的代码操作。           创建并初始化、判断循环双链表是否为空、判断结点p是否为循环双链表的表尾结点的代码操作。     普通双链表用以下代码实现插入的时候,如果插入的结点是最后

    2024年02月16日
    浏览(39)
  • 数据结构(王道)——线性表之静态链表&顺序表和链表的比较

      如何定义一个静态链表     初始化静态链表:   静态链表的查找、插入、删除           创: 销:   增、删:   查:   顺序表、链表该如何选择?  

    2024年02月16日
    浏览(126)
  • 【数据结构】线性表的顺序存储结构及实现——C语言版

    线性表的顺序存储结构称为 顺序表 ,其基本思想是 用一段地址连续的存储单元一次存储线性表的数据元素。 设顺序表的每个元素占用 c 个存储单元,则第 i 个元素的存储地址为: 所以, 只要确定了存储顺序表的起始地址(即基地址),计算任意一个元素的存储地址的时间

    2024年03月15日
    浏览(54)
  • 青岛大学_王卓老师【数据结构与算法】Week03_11_线性表的链式表示和实现11_学习笔记

    本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享,另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础–第3周11–2.5线性表的链式表示和实现

    2024年02月12日
    浏览(40)
  • 数据结构之线性表的类型运用Linear Lists: 数组,栈,队列,链表

    定义 一个最简单,最基本的数据结构。一个线性表由多个相同类型的元素穿在一次,并且每一个元素都一个前驱(前一个元素)和后继(后一个元素)。 线性表的类型 常见的类型:数组、栈、队列、链表 这些不同数据结构的特性可以解决不同种类的问题 题面 题目描述 有

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包