【数据结构】单链表详解

news2024/11/25 20:31:19

☃️个人主页:fighting小泽
🌸作者简介:目前正在学习C语言和数据结构
🌼博客专栏:数据结构
🏵️欢迎关注:评论👊🏻点赞👍🏻留言💪🏻

文章目录

  • 前言
  • 一.链表
    • 1.1 链表的概念及结构
    • 1.2链表的分类
  • 二.单链表的特征
    • 2.1单链表的优缺点
  • 三.SList.h
  • 四.SList.c
    • 4.1单链表的打印
    • 4.2malloc一个新节点
    • 4.3单链表的销毁
    • 4.4单链表的头插头删
    • 4.5单链表的尾插尾删
    • 4.6单链表的查找(修改)
    • 4.7在pos前插入、删除节点
    • 4.8在pos后插入、删除节点
  • 5.结尾

前言

在前面我们已经学习过了有关顺序表的知识,但是我们知道顺序表是存在着一些问题的

问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

所以我们能不能寻求其他的解决方案?

  1. 不扩容
  2. 按需申请释放
  3. 解决头部或者中间插入删除需要挪动数据的问题

顺序表的一切根源就是一块连续的空间,那我们能不能用不连续的空间存储数据呢?

这个时候就要用到我们的链表了。

在这里插入图片描述

一.链表

1.1 链表的概念及结构

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

在这里插入图片描述

在这里插入图片描述

1.2链表的分类

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向的或双向的
    在这里插入图片描述
  2. 带头或者不带头
    在这里插入图片描述
  3. 循环或者非循环
    在这里插入图片描述

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

  1. 无头单向非循环链表
    在这里插入图片描述
  2. 带头双向循环链表
    在这里插入图片描述
  • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  • 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

二.单链表的特征

单链表中,每一个节点存储一个数据和指向下一个节点的指针,最后一个节点指向NULL。
在这里插入图片描述

  • 当我们需要进行头插头删等操作时,需要改变头指针的地址,就需要传入二级指针
  • 当我们想要修改某个结构体变量,(结构体的 next 或者结构体的 data) 时,我们需要传结构体的地址,就需要一级指针。

2.1单链表的优缺点

优点:

  • 单链表增加删除节点简单,遍历的时候不会死循环。
  • 逐个申请、释放空间,没有空间浪费。

缺点:

  • 只能从头到尾遍历。只能找到后继,不能找到前驱,也就是只能前进。

三.SList.h

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLTNode;

void SLTprint(SLTNode* phead);//打印

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

SLTNode* BuyTNode(SLTDataType x);//malloc一个新节点

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

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

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

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//查找某个节点

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在pos指针处插入元素
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//在pos指针后插入元素

void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos指针指向的节点
void SLTEraseAfter(SLTNode* pos);//删除pos指针后一个节点

void SLTDestroy(SLTNode* phead);//删除列表,释放maollc的节点

四.SList.c

4.1单链表的打印

遍历一遍链表打印即可

void SLTprint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

4.2malloc一个新节点

SLTNode* BuyTNode(SLTDataType x)
{
	SLTNode* node = (SLTNode*)malloc(sizeof(SLTNode));
	if (node == NULL)
	{
		perror("malloc");
		return 0;
	}
	node->data = x;
	node->next = NULL;
	return node;
}

4.3单链表的销毁

迭代释放链表节点

void SLDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}

4.4单链表的头插头删

单链表的优势就是头插头删
对于头删,我们要断言二级指针指向的一级指针是否为空。若为空说明该链表没有元素,assert会报错。

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* tail = *pphead;
	*pphead = tail->next;
	free(tail);
}

4.5单链表的尾插尾删

尾插时,需要分类讨论链表是否为空的情况

尾删时,同样需要链表是否为空。同样需要分开讨论仅剩一个头节点或剩余多个节点的情况。若只剩一个节点,删除后将头指针置 NULL 。若剩余多个节点,则删除最后一个节点后将尾节点的 next 置 NULL 。

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyTNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
		return;
	}
	else
	{
		SLTNode* tail = *pphead;
		while ((tail->next) != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SLTPopBack(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* tail = *pphead;
	if (tail->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
		return;
	}
	while (tail->next->next != NULL)
	{
		tail = tail->next;
	}
	free(tail->next);
	tail->next = NULL;
}

4.6单链表的查找(修改)

遍历一遍链表,查找到返回结构体的指针,反之返回NULL
因为已经找到了结构体的指针,所以可以直接修改结构体的变量

查找函数是要配合下面的pos节点插入、删除函数使用的

SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

4.7在pos前插入、删除节点

注意要分类讨论 pos 是否为头节点
此接口不常用,因为单链表的前一个节点不好找,算法效率低

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pos);
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
		return;
	}
	SLTNode* cur = *pphead;
	while (cur)
	{
		if (cur->next == pos)
		{
			SLTNode* newnode = BuyTNode(x);
			newnode->next = cur->next;
			cur->next = newnode;
			return;
		}
		else
			cur = cur->next;
	}
}
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	SLTNode* cur = *pphead;
	if (*pphead == pos)
	{
		SLTPopFront(pphead);
		//*pphead = cur->next;
		//free(cur);
		return;
	}
	while (cur)
	{
		if (cur->next == pos)
		{
			cur->next = pos->next;
			free(pos);
			return;
		}
		else
			cur = cur->next;
	}
}

4.8在pos后插入、删除节点

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);


	SLTNode* newnode = BuyTNode( x);
	newnode->next = pos->next;
	pos->next = newnode;		
}
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	SLTNode* cur = pos->next;
	pos->next = cur->next;
	free(cur);
}

5.结尾

这些就是我给大家分享的关于链表的知识啦,希望我们都能有所收获!
先赞后看,养成习惯!!^ _ ^
码字不易,大家的支持就是我坚持下去的动力,点赞后不要忘了关注我哦!

如有错误,还请您批评改正(。ì _ í。)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499189.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云服务器镜像系统怎么选择?超详细教程

阿里云服务器镜像怎么选择&#xff1f;云服务器操作系统镜像分为Linux和Windows两大类&#xff0c;Linux可以选择Alibaba Cloud Linux&#xff0c;Windows可以选择Windows Server 2022数据中心版64位中文版&#xff0c;阿里云百科来详细说下阿里云服务器操作系统有哪些&#xf…

Buf 教程 - 使用 Protobuf 生成 Golang 代码和 Typescript 类型定义

简介 Buf 是一款更高效、开发者友好的 Protobuf API 管理工具&#xff0c;不仅支持代码生成&#xff0c;还支持插件和 Protobuf 格式化。 我们可以使用 Buf 替代原本基于 Protoc 的代码生成流程&#xff0c;一方面可以统一管理团队 Protoc 插件的版本、代码生成配置&#xff…

测试之路,2023年软件测试市场领域有哪些变化?突破走得更远...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Python自动化测试&…

Linux - 第12节 - 网络编程套接字

1.预备知识 1.1.理解源IP地址和目的IP地址 因特网上的每台计算机都有一个唯一的IP地址&#xff0c;如果一台主机上的数据要传输到另一台主机&#xff0c;那么对端主机的IP地址就应该作为该数据传输时的目的IP地址。但仅仅知道目的IP地址是不够的&#xff0c;当对端主机收到该数…

【Java校招面试】基础知识(七)——数据库

目录 前言一、数据库索引二、数据库锁三、数据库事务四、数据库连接池后记 前言 本篇主要介绍数据库的相关内容。 “基础知识”是本专栏的第一个部分&#xff0c;本篇博文是第六篇博文&#xff0c;如有需要&#xff0c;可&#xff1a; 点击这里&#xff0c;返回本专栏的索引文…

Sourcetree介绍及使用

Sourcetree是一个操作简单但功能强大的免费Git客户端管理工具&#xff0c;可应用在Windows和Mac平台。 Sourcetree的安装&#xff1a; 1.从Sourcetree | Free Git GUI for Mac and Windows 下载SourceTreeSetup-3.4.12.exe&#xff1b; 2.双击SourceTreeSetup-3.4.12.exe&#…

【C++】动态规划

参考博客&#xff1a;动态规划详解 1. 什么是动态规划 动态规划&#xff08;英语&#xff1a;Dynamic programming&#xff0c;简称 DP&#xff09;&#xff0c;是一种在数学、管理科学、计算机科学、经济学和生物信息学中使用的&#xff0c;通过把原问题分解为相对简单的子问…

Linux LED 驱动开发实验

1、LED 灯驱动原理 Linux 下的任何外设驱动&#xff0c;最终都是要配置相应的硬件寄存器。LED 灯驱动最 终也是对 I.MX6ULL 的 IO 口进行配置&#xff0c;在 Linux 下编写驱动要符合 Linux 的驱动框架。I.MX6U-ALPHA 开发板上的 LED 连接到 I.MX6ULL 的 GPIO1_IO03 这个引脚上&…

Day963.如何拆分数据 -遗留系统现代化实战

如何拆分数据 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于如何拆分数据的内容。 如何拆分数据&#xff0c;这个场景在建设新老城区&#xff0c;甚至与其他城市&#xff08;外部系统&#xff09;交互时都非常重要。 作为开发人员&#xff0c;理想中的业务数据存…

C++《vector类的使用介绍》

本文主要介绍vector一些常见的接口函数的使用 文章目录 一、vector的介绍二、vector的使用2.1vector构造函数2.2迭代器的使用2.3空间增长问题2.4增删查改问题 一、vector的介绍 vector是表示可变大小数组的序列容器。就像数组一样&#xff0c;vector也采用的连续存储空间来存储…

比赛记录:Codeforces Round 871 (Div. 4) A~H

传送门:CF A题:A. Love Story 简单比对一下即可解决 #include <bits/stdc.h> using namespace std; typedef long long ll; #define root 1,n,1 #define ls rt<<1 #define rs rt<<1|1 #define lson l,mid,rt<<1 #define rson mid1,r,rt<<1|1 …

模拟银行账户转账业务

文章目录 一、需求分析二、核心代码1. 业务层添加 Spring 事务管理2. 配置类中设置事务管理器3. 开启注解式事务驱动 三、相关截图 一、需求分析 需求&#xff1a; 实现任意两个账户间转账操作&#xff0c;要求当转账过程出现异常时&#xff0c;转账方与被转账方的转账操作同时…

操作系统笔记--CPU调度

1--基本概念 CPU调度&#xff1a; 从进程的就绪队列中挑选一个进程/线程作为CPU将要运行的下一个进程/线程&#xff1b; 在下图中&#xff0c;进程产生状态转换时&#xff08;运行→结束、运行→等待&#xff0c;等等&#xff09;都会发生相应的CPU调度&#xff1b; 内核运行调…

2023/5/7周报

目录 摘要 论文阅读 1、标题和现存问题 2、循环神经网络和传统 LSTM 3、堆叠 LSTM和论文模型结构 4、实验准备 5、结果分析 深度学习 1、TGCN 2、公式 3、伪代码 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇基于注意力机制的堆叠LSTM心电预测算法的论文。模…

1 Python数据分析概况

1 Python数据分析概况 1.1 认识数据分析1.2 熟悉Python数据分析的工具Python数据分析常用类库 1.3 Jupyter Notebook 快捷键 1.1 认识数据分析 数据分析是指用适当的分析方法对收集来的大量数据进行分析&#xff0c;提取有用信息和形成结论&#xff0c;对数据加以详细研究和概…

C语言刷题(1)----指针数组

下面指针选题来源于教材、牛客网。 1.键盘输入一个字符串&#xff0c;编写代码获取字符串的长度并输出&#xff0c;要求使用字符指针实现。 示例&#xff1a; 输入&#xff1a; helloworld 返回值&#xff1a; 10 代码实现 #include<stdio.h> int main (void) {char st…

117-Linux_数据库_事务

事务 一.什么是事务?二.事务的四大特性1.原子性(atomicity)2.一致性(consistency)3.隔离性(isolation)4.持久性(durability) 三.隔离级别1.READ UNCOMMITTED 未提交读2.READ COMMITTED 提交读3.REPEATABLE READ 可重复读4.SERIALIZABLE 可串行化5.查看隔离级别(1)查看当前会话…

HTML5 FormData 方法介绍

XMLHttpRequest 是一个浏览器接口&#xff0c;通过它&#xff0c;我们可以使得 Javascript 进行 HTTP (S) 通信。XMLHttpRequest 在现在浏览器中是一种常用的前后台交互数据的方式。2008年 2 月&#xff0c;XMLHttpRequest Level 2 草案提出来了&#xff0c;相对于上一代&#…

MySQL之约束讲解

1. 主键约束 主键约束要求列的数据唯一&#xff0c;并且不能为空。 主键能够唯一地标识表中的一条记录。 主键和记录之间的关系如同身份证和人之间的关系&#xff0c;它们之间是一一对应的。 1.1 单字段主键 直接在定义列的时候指定主键即可。 create table temp1( num int …

高通 Android 13 兼容extfat模式

Android本身不支持extfat格式 需要通过nofuse 打kernel补丁方式去实现 1、kernel/msm-4.19/arch/arm64/configs/vendor/device-perf_defconfig 增加 diff --git a/kernel/msm-4.19/arch/arm64/configs/vendor/device-perf_defconfig b/kernel/msm-4.19/arch/arm64/configs/ve…