【数据结构】线性表之单链表(讲解实现——带动图理解)

这篇具有很好参考价值的文章主要介绍了【数据结构】线性表之单链表(讲解实现——带动图理解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


单链表

  • 单链表的优点

1.头部和中间插入或删除数据效率高,无需挪动。

2.按照需求申请释放空间,无需担心空间不够用。

  • 单链表的缺点

1.不可以进行下标随机访问。

2.复杂度是O(n)

3.反向遍历困难

单链表是线性表的一种,单链表是链式存储的线性表,不同于单链表,链表在内存空间中不连续,而是由结构体内的next指针下一条数据进行链接🧐

PS:需要源码直接通过目录跳转到最后

单链表主体结构

默认大小与扩容大小还有类型都可以是任意大小或类型

typedef int LLDataType;				//数据类型

typedef struct LinkedList
{
	LLDataType data;					//用于存放数据
	struct LinkedList* next;			//指向下一个数据
}LinkedList;

【数据结构】线性表之单链表(讲解实现——带动图理解)

单链表操作函数介绍

  1. void printLL(LinkedList* Phead); //打印单链表
  2. void LLPushBack(LinkedList** PPhead, LLDataType x); //尾插
  3. void LLPushFront(LinkedList** PPhead, LLDataType x); //头插
  4. void LLPopBack(LinkedList** PPhead); //尾删
  5. void LLPopFront(LinkedList** PPhead); //尾插
  6. LinkedList* LListFind(LinkedList* phead, LLDataType x); //查找
  7. void SListInsertAfter(LinkedList* pos, LLDataType x); //在指定位置后插入
  8. void SListEraseAfter(LinkedList* pos); //删除指定位置后的一个表
  9. void SListDestroy(LinkedList** Phead); //销毁单链表
  10. void SListInsertFront(LinkedList** Phead, LinkedList* pos, LLDataType x); //在指定位置前插入
  11. void SListErase(LinkedList** PPhead, LinkedList* pos); //删除指定位置数据

为了代码方便阅读,讲单链表操作函数全部放在LinkedList.c文件中,将头文件放在LinkedList.h,测试文件test.c 😮

单链表操作函数实现

为了方便调试,建议每写完1-2个函数就进行测试,初始化之后,首先实现print函数可以方便我们进行调试。

单链表的初始化:

LinkedList *Phead = NULL;	//未进行插入之前,头结点指针指向NULL

必须先进性初始化,讲链表指向NULL避免野指针问题。

打印函数

写完插入立马写打印,方便进行调试

void printLL(LinkedList* Phead)
{
	while (Phead!=NULL)
	{
		printf("%d->", Phead->data);
		Phead = Phead->next;
	}
	printf("NULL");
}

单链表插入函数:

头插

头插将新结点插入到头结点

1.如果头结点不为空,头结点改为新插入的结点,原本的第一个位置的结点需要连接到新结点的next。

2.如果头结点为空,那么直接将新结点给到头结点指针phead

  • 既然有插入就一定会有空间不够用的问题,这里用到检查空间是否够用的一个函数
LinkedList* BuyNewNode(LLDataType x)		//申请一个新结点并将next指向空
{
	LinkedList* ret = (LinkedList*)malloc(sizeof(LinkedList));
	if (ret == NULL)
	{
		perror("Malloc:");
		return NULL;
	}
	ret->data = x;
	ret->next = NULL;
	return ret;
}

void LLPushFront(LinkedList** PPhead, LLDataType x)
 // 使用二级指针是因为我们要改变头结点,所有必须要使用头结点的指针来改变。
{
	assert(PPhead);			
	LinkedList* NewNode = BuyNewNode(x);
	if (*PPhead == NULL)
	{
		*PPhead = NewNode;
	}
	else
	{
		NewNode->next = *PPhead;
		*PPhead = NewNode;
	}
}

头结点为空

  • 如果头结点为空,那么直接将新结点给到头结点指针phead

【数据结构】线性表之单链表(讲解实现——带动图理解)

头结点不为空

  • 如果头结点不为空,头结点改为新插入的结点,原本的第一个位置的结点需要连接到新结点的next。

【数据结构】线性表之单链表(讲解实现——带动图理解)

尾插

从最后一个结点后面插入新结点

  • 尾插也会用到申请新结点的函数,这里不重复了
void LLPushBack(LinkedList** PPhead, LLDataType x)
// 使用二级指针是因为我们要改变头结点,所有必须要使用头结点的指针来改变。
{
	assert(PPhead);
	LinkedList* NewNode = BuyNewNode(x);	//也用到了上面的BugNode不重复了
	if (*PPhead == NULL)
	{
		*PPhead = NewNode;
	}
	else
	{
		LinkedList* tail = *PPhead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = NewNode;
	}
}

头结点不为空

  • 如果头结点不为空,先找到最后一个结点(指向NULL的就是最后一个结点),然后将新结点插入到最后一个结点的后面

【数据结构】线性表之单链表(讲解实现——带动图理解)

头结点为空

  • 如果头结点为空,那么直接将新结点给到头结点指针phead(与上面头插头结点为空相同)

【数据结构】线性表之单链表(讲解实现——带动图理解)

指定结点后插入和查找函数

相对上面两个简单,下面插入函数都需搭配查找结点函数使用,查找函数就是遍历,找到返回地址,比较简单,就不多描述了。

LinkedList* LListFind(LinkedList* phead, LLDataType x)
{
	assert(phead);
	LinkedList *ret = phead;
	while (ret->data != x)
	{
		if (ret == NULL)
		{
			return NULL;
		}
		ret = ret->next;
	}
	return ret;
}

void SListInsertAfter(LinkedList* pos, LLDataType x)
{
	assert(pos);
	LinkedList *newnode = BuyNewNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

【数据结构】线性表之单链表(讲解实现——带动图理解)

单链表结点之前插入数据

void SListInsertFront(LinkedList** PPhead, LinkedList* pos, LLDataType x)
{
	assert(pos);
	assert(PPhead);
	if (*PPhead == NULL||*PPhead==pos)
	{
		LLPushFront(PPhead, x);
	}
	else
	{
		LinkedList* cur = *PPhead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		LinkedList* newnode = BuyNewNode(x);
		newnode->next = cur->next;
		cur->next = newnode;
	}
}
  • 头结点为空或插入结点就是头结点指向的结点,直接复用头插函数即可

【数据结构】线性表之单链表(讲解实现——带动图理解)

  • 头结点不为空或插入结点就是头结点指向的结点,找到pos前一个结点进行插入

【数据结构】线性表之单链表(讲解实现——带动图理解)

单链表删除函数

头删

从头部删除数据,将头结点位置删除,将头结点的下一个位置指向头结点

void LLPopFront(LinkedList** PPhead)
{
	assert(PPhead);
	assert(*PPhead);
	LinkedList* temp = (*PPhead)->next;
	free(*PPhead);			//释放掉删除的结点
	*PPhead = temp;
}

【数据结构】线性表之单链表(讲解实现——带动图理解)

尾删

void LLPopBack(LinkedList** PPhead)
{
	assert(PPhead);
	assert(*PPhead);
	if ((*PPhead)->next == NULL)
	{
		*PPhead = NULL;
	}
	else
	{
		LinkedList* tail = *PPhead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}
  • 如果头结点为空,不能进行删除。
  • 如果头结点下一个为空,直接进行删除
  • 如果头结点和头结点的下一个都不为空,删除下一个结点为空的结点,那就需要找到头结点下一个结点的下一个结点为空的结点

【数据结构】线性表之单链表(讲解实现——带动图理解)

指定结点后删除

后面的数据依次向前移动

void SListEraseAfter(LinkedList* pos)
{
	if (pos == NULL)
	{
		printf("没发现此数据");
		return;
	}
	if (pos->next == NULL)
	{
		printf("链表后面已经为空了\n");
		return;
	}
	LinkedList* temp = pos->next;
	pos->next = pos->next->next;
	free(temp);
}
  • 如果pos为空或pos->next为空则提示并返回

【数据结构】线性表之单链表(讲解实现——带动图理解)

指定结点删除

删除指定结点,将指定结点的next连接到指定结点前面的next结点,然后释放指定的结点。

void SListErase(LinkedList**PPhead,LinkedList* pos)
{
	assert(pos);
	assert(PPhead);
	assert(*PPhead);
	if (pos == *PPhead)
	{
		LLPopFront(PPhead);
		return;
	}
	LinkedList* cur = *PPhead;
	while (cur->next != pos)
	{
		cur = cur->next;
	}
	cur->next = cur->next->next;
	free(pos);
}
  • 头结点指针指向的结点等于pos,复用尾插函数

【数据结构】线性表之单链表(讲解实现——带动图理解)

销毁单链表

将每个结点依次释放。最后将头结点制空。

void SListDestroy(LinkedList** PPhead)
{
	assert(*PPhead);
	if (*PPhead == NULL)
	{
		printf("已经是空链表了\n");
		return;
	}
	while ((*PPhead)->next != NULL)
	{
		LinkedList* tep = (*PPhead)->next;
		(*PPhead)->next = (*PPhead)->next->next;
		free(tep);
	}
	free(*PPhead);
	*PPhead = NULL;
}

【数据结构】线性表之单链表(讲解实现——带动图理解)


文件分类

🌞🌞为了使代码更有阅读性,我们不建议把所有函数写在一个文件里,所以这里分成三个文件,模块化管理

test.c

测试文件

#include "LinkedList.h"

int main()
{
	LinkedList *Phead = NULL;
	LLPushFront(&Phead, 8);
	LLPushBack(&Phead,2);
	LLPushBack(&Phead,3);
	LLPushBack(&Phead,4);
	LLPushBack(&Phead,5);
	//LLPopBack(&Phead);
	//LLPopFront(&Phead);
	//LLPopBack(&Phead);
	//LLPopFront(&Phead);
	//LLPopFront(&Phead);
	LinkedList* temp = LListFind(Phead, 3);
	SListInsertAfter(temp,88);
	temp = LListFind(Phead, 4);
	SListEraseAfter(temp);
	temp = LListFind(Phead, 4);

	SListInsertFront(&Phead,temp, 99);
	printLL(Phead);
	temp = LListFind(Phead, 99);
	SListErase(&Phead, temp);
	SListDestroy(&Phead);
}

LinkedList.c

将所有单链表需要的函数封装在此文件下⭐

#include "LinkedList.h"

LinkedList* BuyNewNode(LLDataType x)
{
	LinkedList* ret = (LinkedList*)malloc(sizeof(LinkedList));
	if (ret == NULL)
	{
		perror("Malloc:");
		return NULL;
	}
	ret->data = x;
	ret->next = NULL;
	return ret;
}

void LLPushBack(LinkedList** PPhead, LLDataType x)
{
	assert(PPhead);
	LinkedList* NewNode = BuyNewNode(x);
	if (*PPhead == NULL)
	{
		*PPhead = NewNode;
	}
	else
	{
		LinkedList* tail = *PPhead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = NewNode;
	}
}

void LLPushFront(LinkedList** PPhead, LLDataType x)
{
	assert(PPhead);
	LinkedList* NewNode = BuyNewNode(x);
	if (*PPhead == NULL)
	{
		*PPhead = NewNode;
	}
	else
	{
		NewNode->next = *PPhead;
		*PPhead = NewNode;
	}
}

void LLPopBack(LinkedList** PPhead)
{
	assert(PPhead);
	assert(*PPhead);
	if ((*PPhead)->next == NULL)
	{
		*PPhead = NULL;
	}
	else
	{
		LinkedList* tail = *PPhead;
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

void LLPopFront(LinkedList** PPhead)
{
	assert(PPhead);
	assert(*PPhead);
	LinkedList* temp = (*PPhead)->next;
	free(*PPhead);
	*PPhead = temp;
}

void printLL(LinkedList* Phead)
{
	while (Phead!=NULL)
	{
		printf("%d->", Phead->data);
		Phead = Phead->next;
	}
	printf("NULL");
}

LinkedList* LListFind(LinkedList* phead, LLDataType x)
{
	assert(phead);
	LinkedList *ret = phead;
	while (ret->data != x)
	{
		if (ret == NULL)
		{
			return NULL;
		}
		ret = ret->next;
	}
	return ret;
}

void SListInsertAfter(LinkedList* pos, LLDataType x)
{
	assert(pos);
	LinkedList *newnode = BuyNewNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SListInsertFront(LinkedList** PPhead, LinkedList* pos, LLDataType x)
{
	assert(pos);
	assert(PPhead);
	if (*PPhead == NULL||*PPhead==pos)
	{
		LLPushFront(PPhead, x);
	}
	else
	{
		LinkedList* cur = *PPhead;
		while (cur->next != pos)
		{
			cur = cur->next;
		}
		LinkedList* newnode = BuyNewNode(x);
		newnode->next = cur->next;
		cur->next = newnode;
	}
}

void SListEraseAfter(LinkedList* pos)
{
	if (pos == NULL)
	{
		printf("没发现此数据");
		return;
	}
	if (pos->next == NULL)
	{
		printf("链表后面已经为空了\n");
		return;
	}
	LinkedList* temp = pos->next;
	pos->next = pos->next->next;
	free(temp);
}


void SListErase(LinkedList**PPhead,LinkedList* pos)
{
	assert(pos);
	assert(PPhead);
	assert(*PPhead);
	if (pos == *PPhead)
	{
		LLPopFront(PPhead);
		return;
	}
	LinkedList* cur = *PPhead;
	while (cur->next != pos)
	{
		cur = cur->next;
	}
	cur->next = cur->next->next;
	free(pos);
}
 
//void SListDestroy(LinkedList** Phead)
//{
//	assert(Phead);
//	if (*Phead == NULL)
//	{
//		printf("已经是空链表了");
//		return;
//	}
//	while ((*Phead) != NULL)
//	{
//		LinkedList* temp = *Phead;
//		*Phead = (*Phead)->next;
//		free(temp);
//	}
//}

void SListDestroy(LinkedList** PPhead)
{
	assert(*PPhead);
	if (*PPhead == NULL)
	{
		printf("已经是空链表了\n");
		return;
	}
	while ((*PPhead)->next != NULL)
	{
		LinkedList* tep = (*PPhead)->next;
		(*PPhead)->next = (*PPhead)->next->next;
		free(tep);
	}
	free(*PPhead);
	*PPhead = NULL;
}

LinkedList.h

将主程序所需要的函数全部在头文件中声明,增加代码阅读性⭐

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int LLDataType;

typedef struct LinkedList
{
	LLDataType data;
	struct LinkedList* next;
}LinkedList;

void printLL(LinkedList* Phead);
void LLPushBack(LinkedList** PPhead, LLDataType x);
void LLPushFront(LinkedList** PPhead, LLDataType x);
void LLPopBack(LinkedList** PPhead);
void LLPopFront(LinkedList** PPhead);
LinkedList* LListFind(LinkedList* phead, LLDataType x);
void SListInsertAfter(LinkedList* pos, LLDataType x);
void SListEraseAfter(LinkedList* pos);
void SListDestroy(LinkedList** Phead);
void SListInsertFront(LinkedList** Phead, LinkedList* pos, LLDataType x);
void SListErase(LinkedList** PPhead, LinkedList* pos);

撒花

这就是实现单链表的全部内容了,创作不易,还请各位小伙伴多多点赞👍关注收藏⭐,以后也会更新各种关于c语言,数据结构的博客,撒花!文章来源地址https://www.toymoban.com/news/detail-458583.html

到了这里,关于【数据结构】线性表之单链表(讲解实现——带动图理解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【数据结构】线性表之栈、队列

    前面两篇文章讲述了关于线性表中的顺序表与链表,这篇文章继续讲述线性表中的 栈和队列。 这里讲述的两种线性表与前面的线性表不同,只允许在一端入数据,一段出数据,详细内容请看下面的文章。 顺序表与链表两篇文章的链接: 线性表之顺序表 线性表之链表 注意:

    2024年02月06日
    浏览(57)
  • 数据结构:线性表之-顺序表

    目录 1.线性表概念 1.1 什么是顺序列表 1.2 线性表 2.顺序表实现 将有以下功能: 详细过程 顺序表的动态存储 顺序表初始化 尾插 扩容 头插 更改后的尾插 尾删 头删 打印 释放内存 优化顺序表 (任意位置插入删除) 优化后的头插尾插 优化后的头删尾删 查找和删除 进行装饰(菜单

    2024年02月10日
    浏览(47)
  • 【数据结构】线性表之链表

    上一篇文章讲述了线性表中的顺序表,这篇文章讲述关于链表的定义、类别、实现、多种不同链表的优缺点和链表与顺序表的优缺点。 关于上一篇文章的链接:线性表之顺序表 链表是一种物理存储结构上 非连续、非顺序 的存储结构,数据元素的逻辑顺序是通过链表中的指针

    2024年02月05日
    浏览(53)
  • 【数据结构】线性表之顺序表

    线性表是 n (n = 0) 个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列… 线性表在 逻辑上是线性结构 ,也就说是连续的一条直线。但是在 物理结构 上并 不一定 是连续的线性表在物理上存储时,通常以

    2024年02月04日
    浏览(53)
  • 数据结构——线性表之顺序表

    目录 一.线性表 二.顺序表实现  2.1 概念及结构  2.2 动态顺序表 2.2.1 初始化与销毁函数 2.2.2 打印函数 2.2.3 尾插函数 2.2.4 尾删函数 2.2.5 扩容函数 2.2.6 头插函数 2.2.7 头删函数 2.2.8 任意位置插入函数 2.2.9 查找函数 2.2.10 任意位置删除函数  2.2.11 修改函数 三.完整代码 四.力扣

    2024年02月07日
    浏览(44)
  • 数据结构: 线性表(无哨兵位单链表实现)

    在介绍链表之前, 先回顾一下顺序表的优缺点 顺序表的优点 : 存储密度高: 无需为表示表中元素之间的逻辑关系而增加额外的存储空间. 随机访问: 通过首地址和元素序号可以在时间 O ( 1 ) O(1) O ( 1 ) 内找到指定的元素. 命中率高:CPU每次取一定大小空间放入缓存,连续的空间命

    2024年02月14日
    浏览(35)
  • 数据结构:线性表之-单向链表(无头)

    目录 什么是单向链表 顺序表和链表的区别和联系 顺序表: 链表: 链表表示(单项)和实现 1.1 链表的概念及结构 1.2单链表(无头)的实现 所用文件 将有以下功能: 链表定义 创建新链表元素 尾插 头插 尾删 头删 查找-给一个节点的指针 改 pos位置之前插入 删除pos位置的值 成品

    2024年02月09日
    浏览(63)
  • C数据结构-线性表之顺序表

    首先我们创建3个文件,分别如下: liner_data --sqlist.c --sqlist.h --test.c 下面编写sqlist.c文件:函数实现的功能 test.c文件:main函数的执行入口 c语言程序编译的过程如下: 预编译-编译-汇编-连接 汇编:gcc -c sqlist.c -o sqlist.o gcc -c test.c -o test.o 连接:可执行文件:gcc sqlist.o test.o -o

    2024年02月09日
    浏览(88)
  • 【数据结构之线性表】单链表实现图书管理系统

            本次实验是在DEV C++软件上进行实现的。语言采用的是c++语言,但在整体上与c语言大致相似(不管用什么语言实现,思想是不变的)。         此次实现的整体思路:首先定义图书这个抽象数据类型,并且定义节点抽象数据类型(根据这些抽象数据类型对下面的数

    2024年02月08日
    浏览(38)
  • 数据结构:线性表之-循环双向链表(万字详解)

    目录 基本概念 1,什么是双向链表 2,与单向链表的区别 双向链表详解 功能展示: 1. 定义链表 2,创建双向链表 3,初始化链表 4,尾插 5,头插 6,尾删 判断链表是否被删空 尾删代码 7,头删 8,pos位置之前插入 优化后的头插 优化后的尾插 9,删除pos位置的节点 优化后的尾删 优

    2024年02月09日
    浏览(49)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包