数据结构笔记第2篇:单向链表

news2025/1/19 20:33:37

1、链表的概念及结构

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

就像图中的小火车,每节车厢都是一个节点,每个节点都存储着一个数据。它们本身并不是顺序存储的,而是由詹式车钩相互连起来的。而在链表中这个詹式车钩就是指针链接

火车在淡季时会减去几个车厢,而在旺季时会加上几个车厢。火车是可以灵活改变的。链表也是同理。

每个节点是在不同的空间存放,可能乱七八糟,但是有了指针链接将他们连接起来,从而在逻辑结构上好像是连续的。

在链表里,每节 "车厢" 是什么样子的呢?

与顺序表不同的是,每节 "车厢" 都是独立申请下来的,我们称之为 "节点/结点"。

节点的组成主要有两部分:当前节点要保存的数据和保存的下一个节点的地址(指针变量)。

图中指针变量plist保存的是第一个节点的地址,我们称plist此时 "指向" 第一个节点。如果我们想让plist指向第二个节点就让plist保存第二个节点的地址:0x0012FFA0。

为什么还需要指针变量来保存下一个节点的地址?

链表的每一个节点都是独立申请的(即需要插入一个数据时才去申请一块节点的空间),我们需要通过指针变量来保存下一个节点的位置才能从当前节点找到下一个节点。

2、单向链表的实现

结合前面学到的结构体知识,我们可以通过结构体来创建每个节点对应的结构体代码:

typedef int SLTypeData;
//创建每个节点类型
typedef struct SList
{
	SLTypeData data;  //存储数据
	struct SList* next;  //下一个节点的地址
}*pList;

这就是链表每个节点的类型创建,关于单向链表的接口都有以下这些。

void SLPushBack(pList* phead, SLTypeData val);//链表节点尾部插入
pList SLBuyNode(int val);//申请节点空间
void SLPrint(pList* phead);//打印链表每个节点的数据
void SLPushFront(pList* phead, SLTypeData val);//链表节点头部插入
void SLPopBack(pList* phead);//链表节点尾部删除
void SLPopFront(pList* phead);//链表节点头部删除
void SLInsert(pList* phead, pList pos, SLTypeData val);//指定位置之前插入
pList SLfind(pList* phead, SLTypeData val);//查找该存储数据的节点并返回
void SLInsertAfter(pList pos, SLTypeData val);//指定位置之后插入
void SLEarse(pList* phead, pList pos);//指定位置节点删除
void SLDesTroy(pList* phead);//链表销毁

完整代码:分别在三个文件,test.c文件调用函数、pList.c实现函数、pList.h函数声明。

pList.h 声明:

#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int SLTypeData;

//创建每个节点类型
typedef struct SList
{
	SLTypeData data;  //存储数据
	struct SList* next;  //下一个节点的地址
}*pList;

void SLPushBack(pList* phead, SLTypeData val);//链表节点尾部插入
pList SLBuyNode(int val);//申请节点空间
void SLPrint(pList* phead);//打印链表每个节点的数据
void SLPushFront(pList* phead, SLTypeData val);//链表节点头部插入
void SLPopBack(pList* phead);//链表节点尾部删除
void SLPopFront(pList* phead);//链表节点头部删除
void SLInsert(pList* phead, pList pos, SLTypeData val);//指定位置之前插入
pList SLfind(pList* phead, SLTypeData val);//查找该存储数据的节点并返回
void SLInsertAfter(pList pos, SLTypeData val);//指定位置之后插入
void SLEarse(pList* phead, pList pos);//指定位置节点删除
void SLDesTroy(pList* phead);//链表销毁

pList.c 函数实现:

#include "pList.h"

pList SLBuyNode(SLTypeData val)
{
	pList ret = (pList)malloc(sizeof(struct SList));
	if (ret == NULL)
	{
		perror("malloc");
		return NULL;
	}
	ret->data = val;
	ret->next = NULL;
	return ret;
}

void SLPushBack(pList* phead, SLTypeData val)
{
	assert(phead != NULL);
	pList node = SLBuyNode(val);
	if (node == NULL)
	{
		perror("SLBuyNode");
		return;
	}
	if (*phead == NULL)
	{
		*phead = node;
		return;
	}
	pList pcur = *phead;
	//找到尾节点
	while (pcur->next)
	{
		pcur = pcur->next;
	}
	//给尾结点下一个节点的地址
	pcur->next = node;
}

void SLPrint(pList* phead)
{
	assert(phead);
	assert(*phead);
	pList pcur = *phead;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

void SLPushFront(pList* phead, SLTypeData val)
{
	assert(phead != NULL);
	pList node = SLBuyNode(val);
	if (node == NULL)
	{
		perror("SLBuyNode");
		return;
	}
	//头部插入操作
	node->next = *phead;
	*phead = node;
}

void SLPopBack(pList* phead)
{
	assert(*phead != NULL);
	//首先判断一下链表是否只有一个节点的情况,如果是则直接释放头结点
	if ((*phead)->next == NULL)
	{
		free(*phead);
		*phead = NULL;
		return;
	}
	pList pcur = *phead;
	while (pcur->next->next)
	{
		pcur = pcur->next;
	}
	//完成尾部删除
	free(pcur->next);
	pcur->next = NULL;
}

void SLPopFront(pList* phead)
{
	assert(phead != NULL);
	pList pcur = *phead;
	*phead = (*phead)->next;
	free(pcur);
	pcur = NULL;
}

void SLInsert(pList* phead, pList pos, SLTypeData val)
{
	assert(phead);
	assert(*phead);
	assert(pos);
	pList node = SLBuyNode(val);
	//判断指定位置是否等于头结点
	if (pos == *phead)
	{
		node->next = *phead;
		*phead = node;
		return;
	}
	pList prve = *phead;
	//找到链表中pos位置之前的节点
	while (prve->next != pos)
	{
		prve = prve->next;
	}
	//插入操作
	prve->next = node;
	node->next = pos;
}

pList SLfind(pList* phead, SLTypeData val)
{
	assert(phead);
	pList pcur = *phead;
	while (pcur)
	{
		if (pcur->data == val)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;//如果遍历了一遍链表未找到存储该数据的节点则返回NULL 
}

void SLInsertAfter(pList pos, SLTypeData val)
{
	assert(pos);
	pList node = SLBuyNode(val);
	node->next = pos->next;
	pos->next = node;
}

void SLEarse(pList* phead, pList pos)
{
	assert(phead);
	assert(*phead);
	assert(pos);
	if (pos == *phead)
	{
		*phead = (*phead)->next;
		free(pos);
		return;
	}
	pList prev = *phead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}

void SLDesTroy(pList* phead)
{
	assert(phead);
	assert(*phead);
	pList pcur = *phead;
	while (pcur)
	{
		pList next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*phead = NULL;
	printf("销毁成功\n");
}

test.c 函数调用:

#include "pList.h"

void SLttext()
{
	pList plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPrint(&plist); //打印:1 -> 2 -> 3 -> 4 -> NULL

	SLPushFront(&plist, 5);
	SLPushFront(&plist, 6);
	SLPushFront(&plist, 7);
	SLPrint(&plist);//打印:7 -> 6 -> 5 -> 1 -> 2 -> 3 -> 4 -> NULL

	SLPopBack(&plist);
	SLPrint(&plist);//打印:7 -> 6 -> 5 -> 1 -> 2 -> 3 -> NULL

	SLPopFront(&plist);
	SLPrint(&plist);//打印:6 -> 5 -> 1 -> 2 -> 3 -> NULL

	pList find = SLfind(&plist, 3);
	SLInsert(&plist, find, 11);
	SLPrint(&plist);//打印:6 -> 5 -> 1 -> 2 -> 11 -> 3 -> NULL

    find = SLfind(&plist, 1);
	SLInsertAfter(find, 12);
	SLPrint(&plist);//打印:6 -> 5 -> 1 -> 12 -> 2 -> 11 -> 3 -> NULL
	
	find = SLfind(&plist, 5);
	SLEarse(&plist, find);
	SLPrint(&plist);//打印:6 -> 1 -> 12 -> 2 -> 11 -> 3 -> NULL

	SLDesTroy(&plist);
}
int main()
{
	SLttext();
	return 0;
}

3、链表的分类

链表的结构非常多样,以下结构组合起来就有8种(2 x 2 x 2)链表结构。

链表结构:

1. 单向或双向

2. 带头或不带头

3. 循环或不循环

虽然有这么多链表的结构,但是我们实际中最常用的还是两种结构:单链表和双向带头循环链表

1. 不带头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用链表数据结构,都是带头双向循环链表。另外这个结构虽然复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现就知道了。

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

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

相关文章

python自动化办公之PyPDF2

用到的库&#xff1a;PyPDF2 实现效果&#xff1a;打开pdf文件&#xff0c;把每一页的内容读出来 代码&#xff1a; import PyPDF2 # 打开pdf文件 fileopen(friday.pdf,rb) # 创建pdf文件阅读器对象 readerPyPDF2.PdfReader(file) # 获取pdf文件的总页数 total_pageslen(rea…

SonicSense:声学振动丰富机器人的物体感知能力

在通过声学振动进行物体感知方面&#xff0c;尽管以往的研究已经取得了一些有希望的结果&#xff0c;但目前的解决方案仍然受限于几个方面。首先&#xff0c;大多数现有研究集中在只有少数&#xff08;N < 5&#xff09;基本物体的受限设置上。这些物体通常具有均质材料组成…

抖音直播自动点赞脚本:让点赞变得简单

抖音直播自动点赞脚本&#xff1a;让点赞变得简单 简介 点赞是社交媒体上表达喜爱的一种方式&#xff0c;尤其在抖音这样的平台上&#xff0c;点赞不仅能够增加主播的人气&#xff0c;还能鼓励他们创作更多优质内容。然而&#xff0c;手动点赞往往既耗时又费力。为了解决这个…

远程连接mysql等支持网络服务的数据库

1.ubuntu服务器上的mysql用datagrip连接需要专门去给mysql在服务器上运行的端口开放安全组吗 在使用 DataGrip 或任何其他数据库管理工具远程连接到 Ubuntu 服务器上的 MySQL 时&#xff0c;确实需要确保服务器的防火墙和安全组设置允许从你的 IP 地址访问 MySQL 所运行的端口&…

在Linux (Ubuntu 16) 下安装LabVIEW

用户尝试在Ubuntu 16操作系统上安装LabVIEW&#xff0c;但找不到合适的安装文件来支持Ubuntu。已经下载了运行时文件&#xff0c;并尝试将.rpm包转换为.deb包并安装在Ubuntu上。然而&#xff0c;安装完成后&#xff0c;没有在应用程序中看到LabVIEW的图标。 用户希望能够在Ubu…

Apache Ranger 2.4.0 集成Hive 3.x(Kerbos)

一、解压tar包 tar zxvf ranger-2.4.0-hive-plugin.tar.gz 二、修改install.propertis POLICY_MGR_URLhttp://localhost:6080REPOSITORY_NAMEhive_repoCOMPONENT_INSTALL_DIR_NAME/BigData/run/hiveCUSTOM_USERhadoop 三、进行enable [roottv3-hadoop-01 ranger-2.4.0-hive…

【SGX系列教程】(八)Intel-SGX 官方示例分析(SampleCode)——Seal Unseal

文章目录 一.Seal Unseal原理介绍1.1 Intel SGX supported Sealing Policies 二.源码分析2.1 README2.2 重点代码分析2.2.1 主要代码模块交互流程分析2.2.2 App/App.cpp2.2.3 Enclave_Seal/Enclave_Seal.cpp2.2.4 Enclave_Unseal/Enclave_Unseal.cpp 2.3 总结 三.参考文献四.感…

Debugging using Visual Studio Code

One of the key features of Visual Studio Code is its great debugging support. VS Code’s built-in debugger helps accelerate your edit, compile, and debug loop. Debugger extensions VS Code 内置了对 Node.js 运行时的调试支持,可以调试 JavaScript、TypeScript…

HDFS学习

3.5 HDFS存储原理 3.5.1 冗余数据保存 作为一个分布式文件系统&#xff0c;为了保证系统的容错性和可用性&#xff0c;HDFS采用了多副本方式对数据进行冗余存储&#xff0c;通常一个数据块的多个副本会被分布到不同的数据节点上。 如图所示&#xff0c;数据块1被分别存放到…

【IVI】CarService启动-Android13

【IVI】CarService启动-Android13 1、CarServiceImpl启动概述2、简要时序图 1、CarServiceImpl启动概述 【IVI】CarService启动&#xff1a; CarServiceHelperService中绑定CarServiceICarImpl初始化各种服务 packages/services/Car/README.md 2、简要时序图

RabbitMQ-交换机的类型以及流程图练习-01

自己的飞书文档:‌‍‬‍‬‍​‍‬​⁠‍​​​‌⁠​​‬‍​​​‬‬‌​‌‌​​&#xfeff;​​​​&#xfeff;‍​‍​‌&#xfeff;⁠‬&#xfeff;&#xfeff;&#xfeff;​RabbitMQ的流程图和作业 - 飞书云文档 (feishu.cn) 作业 图片一张 画rabbit-mq 消息发…

imx6ull/linux应用编程学习(6)jpeg和png的图片显示

1.JPEG图片显示 JPEG&#xff08;Joint Photographic Experts Group&#xff09;是由国际标准组织为静态图像所建立的第一个国际数字图像压缩标准&#xff0c;也是至今一直在使用的、应用最广的图像压缩标准。JPEG 由于可以提供有损压缩&#xff0c;因此压缩比可以达到其他传统…

sqlserver开启CDC

1、背景 由于需要学习flink cdc&#xff0c;并且数据选择sqlserver&#xff0c;所以这里记录sqlserver的cdc开启操作步骤。 2、基础前提 官方介绍地址&#xff1a;https://learn.microsoft.com/zh-cn/sql/relational-databases/track-changes/enable-and-disable-change-dat…

快递物流仓库管理系统java项目springboot和vue的前后端分离系统java课程设计java毕业设计

文章目录 快递物流仓库管理系统一、项目演示二、项目介绍三、部分功能截图四、部分代码展示五、底部获取项目源码&#xff08;9.9&#xffe5;带走&#xff09; 快递物流仓库管理系统 一、项目演示 快递物流仓库管理系统 二、项目介绍 语言: Java 数据库&#xff1a;MySQL 前…

论文阅读《U-KAN Makes Strong Backbone for MedicalImage Segmentation and Generation》

Abstract U-Net 已成为图像分割和扩散概率模型等各种视觉应用的基石。虽然通过结合transformer或 MLP&#xff0c;U-Net 已经引入了许多创新设计和改进&#xff0c;但仍然局限于线性建模模式&#xff0c;而且可解释性不足。为了应对这些挑战&#xff0c;我们的直觉受到了 Kolm…

PotPlayer安装及高分辨率设置

第1步&#xff1a; 下载安装PotPlayer软件 PotPlayer链接&#xff1a;https://pan.baidu.com/s/1hW168dJrLBonUnpLI6F3qQ 提取码&#xff1a;z8xd 第2步&#xff1a; 下载插件&#xff0c;选择系统对应的位数进行运行&#xff0c;该文件不能删除&#xff0c;删除后将失效。 …

云计算HCIE+RHCE学员的学习分享

大一下学期&#xff0c;我从学长嘴里了解到誉天教育&#xff0c;当时准备考RHCE&#xff0c;我也了解了很多培训机构&#xff0c;然后学长强烈给我推荐誉天&#xff0c;我就在誉天报名了RHCE的课程。 通过杨峰老师的教学&#xff0c;我学到了许多Linux知识&#xff0c;也了解了…

【87 backtrader期权策略】基于50ETF期权的covered-call-strategy

前段时间有读者希望能够实现一个期权策略的模板,这段时间通过akshare下载了期权的数据,并进行了清洗,写了一个最简单的期权策略,供大家参考。 策略逻辑: 这是151 trading strategies中的一个期权策略。 买入50ETF基金,手续费按照万分之二计算,一直持有卖出一个最远期的…

Analyze an ORA-12801分析并行 parallel 12801 实际原因

"ORA-06512: at "PKG_P_DATA", line 19639 ORA-06512: at "PKG_P_DATA", line 19595 ORA-06512: at "PKG_P_DATA", line 14471-JOB 调用 -ORA-12801: error signaled in parallel query server P009, instance rac2:dwh2 (2) Error: ORA-12…

基于YOLOv9的PCB板缺陷检测

数据集 PCB缺陷检测&#xff0c;我们直接采用北京大学智能机器人开放实验室数据提供的数据集&#xff0c; 共六类缺陷 漏孔、鼠咬、开路、短路、杂散、杂铜 已经对数据进行了数据增强处理&#xff0c;同时按照YOLO格式配置好&#xff0c;数据内容如下 模型训练 ​ 采用YOLO…