快速通关单链表秘籍

news2025/5/16 13:15:21

1.单链表概念与结构

1.1 概念

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

光看定义有点不好理解,我们举个简单例子!

我们都看过火车吧,我们看到的火车是有一节一节的车厢相连接,这个链接的纽带我们可以抽象的看作指针链接,我们通过车厢链接去找到下一节车厢,在单链表中我们可以通过指针链接去找到下一个节点。当我们需要增加(或减少)节车厢时,可以额外增加(或减少)几节。链表也是如此,这也是链表相对于顺序表的一大优点:可以随意增(减)容。

由此我们可以抽象出单链表的基本结构:

 1.2 结点结构

火车的每一节车厢叫车厢,链表的每一节“车厢”叫结点。

链表里面的每一节“车厢”都是独立像内存申请的空间,我们称作“结点”。

根据这幅图,我们plist就是头结点,通过plist里的存放的地址我们可以找到下一结点的位置,要找到下一节点,我们就必须的有它对应的地址,因此节点不仅存放了相应的数据还有下一结点的地址!

所以结点的结构:数据+下一结点的地址。

同理我们也可以去修改对应的地址,去访问不同的结点空间。例如:修改plist的值,plist=0x0012FFA0,再去访问结点访问的就是数据2对应的结点了。

注意:结点都是独立申请的(即需要插入数据时才去申请一块结点的空间),我们需要通过指针变量保存下一结点的地址才能找到下一个结点。

1.3 链表的性质

1. 链表在逻辑结构上是连续的,物理结构是不连续的!

2. 结点是独立的,是从堆上申请的。

3. 从堆上申请下来的结点,是按一定策略分配的,可能申请的空间连续,也可能不连续。

这里有人可能不知道什么是堆区,这里留了一张大概的图片有助于理解:

 1.4 链表结点的结构

typedef int SLTDataType;//这里重命名,可以更改数据的类型
typedef struct SListNode
{
	SLTDataType data;//节点里面的数据
	struct SListNode* Next;//节点里存放的下一个结点的地址
}SLTNode;

2. 链表的实现 

2.1 手动构造一个链表

void test()
{
	SLTNode SL;
	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
	node1->data = 1;
	node2->data = 2;
	node3->data = 3;
	node4->data = 4;
	node1->next = node2;
	node2->next = node3;
	node3->next = node4;
	node4->next = NULL;
    SLTNode* plist = node1;//头结点
}

2.2 尾插

//头插
void SLPushFront(SLTNode** pphead, SLDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

2.3 头插

//头插
void SLPushFront(SLTNode** pphead, SLDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

 2.4 尾删

//尾删
void SLPopBack(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	SLTNode* prev = NULL;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		while (pcur->next)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		prev->next = NULL;
		free(pcur);
		pcur = NULL;
	}
}

 2.5 头删

//头删
void SLPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

2.6 指定位置之前插入数据 

//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	SLTNode* prev = *pphead;
	if (*pphead == pos)
		SLPushFront(pphead, x);
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

2.7 在指定位置之后插入数据

//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

2.8 删除指定位置之前的数据

//删除指定位置之前的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);
	SLTNode* prev = *pphead;
	//当只有一个节点的时候
	if (*pphead == pos)
		SLPopFront(pphead);
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

2.9 删除pos之后的结点

//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	SLTNode* next = pos->next;
	pos->next = pos->next->next;
	free(next);
	next = NULL;
}

2.10 销毁链表

//销毁链表
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	SLTNode* next = *pphead;
	while (pcur)
	{
		next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3. 单链表实现代码

SList.h文件

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

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

//头结点的位置会发生改变,即形参会影响实参,传二级指针
//尾插
void SLPushBack(SLTNode** pphead,SLTDataType x);
//打印链表
void SLPrint(SLTNode* phead);
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x);
//尾删
void SLPopBack(SLTNode** pphead);
//头删
void SLPopFront(SLTNode** pphead);
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除指定位置之前的数据
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos);
//销毁链表
void SLTDestroy(SLTNode** pphead);

 SList.c文件

#include "SList.h"

SLTNode* BuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
}
//打印链表
void SLPrint(SLTNode* phead)
{
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
//尾插
void SLPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuyNode(x);
	//这里需要讨论两种情况:空链表和非空链表
	SLTNode* pcur = *pphead;
	//空链表
	if(*pphead==NULL)
	{
		*pphead = newnode;
	}
	else//非空链表
	{
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next=newnode;
	}
}
//头插
void SLPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}
//尾删
void SLPopBack(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	SLTNode* prev = NULL;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		while (pcur->next)
		{
			prev = pcur;
			pcur = pcur->next;
		}
		prev->next = NULL;
		free(pcur);
		pcur = NULL;
	}
}
//头删
void SLPopFront(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}
//查找
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
//在指定位置之前插⼊数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyNode(x);
	SLTNode* prev = *pphead;
	//当只有一个结点的时候
	if (*pphead == pos)
		SLPushFront(pphead, x);
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}
//在指定位置之后插⼊数据
void SLTInsertAfter(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead && pos);
	SLTNode* newnode = BuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除指定位置之前的数据
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead && pos);
	SLTNode* prev = *pphead;
	//当只有一个节点的时候
	if (*pphead == pos)
		SLPopFront(pphead);
	else
	{
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}
//删除pos之后的结点
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	SLTNode* next = pos->next;
	pos->next = pos->next->next;
	free(next);
	next = NULL;
}
//销毁链表
void SLTDestroy(SLTNode** pphead)
{
	assert(pphead);
	SLTNode* pcur = *pphead;
	SLTNode* next = *pphead;
	while (pcur)
	{
		next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

test.c 文件

#include "SList.h"

//void test()
//{
//	SLTNode sl;
//	SLTNode* node1 = (SLTNode*)malloc(sizeof(SLTNode));
//	SLTNode* node2 = (SLTNode*)malloc(sizeof(SLTNode));
//	SLTNode* node3 = (SLTNode*)malloc(sizeof(SLTNode));
//	SLTNode* node4 = (SLTNode*)malloc(sizeof(SLTNode));
//	node1->data = 1;
//	node2->data = 2;
//	node3->data = 3;
//	node4->data = 4;
//	node1->next = node2;
//	node2->next = node3;
//	node3->next = node4;
//	node4->next = NULL;
//}
void test()
{
	SLTNode SL;
	SLTNode* plist = NULL;
	SLPushBack(&plist, 1);
	SLPushBack(&plist, 2);
	SLPushBack(&plist, 3);
	SLPushBack(&plist, 4);
	SLPrint(plist);
	//SLPushFront(&plist, 1);
	//SLPushFront(&plist, 2);
	//SLPushFront(&plist, 3);
	//SLPushFront(&plist, 4);
	//SLPopBack(&plist);
	//SLPopBack(&plist);
	//SLPopBack(&plist);
	//SLPopBack(&plist);
	//SLPopFront(&plist);
	//SLPrint(plist);
	//SLPopFront(&plist);
	//SLPrint(plist);
	//SLPopFront(&plist);
	//SLPrint(plist);
	//SLPopFront(&plist);
	//SLPrint(plist);

	SLTNode* find = SLTFind(plist, 3);
	//if (find)
	//	printf("找到了\n");
	//else
	//	printf("未找到\n");
	//SLTInsert(&plist, find, 6);
	//SLPrint(plist);
	//SLTInsertAfter(&plist, find, 5);
	//SLPrint(plist);
	//SLTErase(&plist, find);
	//SLPrint(plist);
	SLTEraseAfter(find);
	SLPrint(plist);
}
int main()
{
	test();
	return 0;
}

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

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

相关文章

springboot+vue实现在线书店(图书商城)系统

今天教大家如何设计一个图书商城 , 基于目前主流的技术&#xff1a;前端vue&#xff0c;后端springboot。 同时还带来的项目的部署教程。 视频演示 在线书城 图片演示 一. 系统概述 商城是一款比较庞大的系统&#xff0c;需要有商品中心&#xff0c;库存中心&#xff0c;订单…

Spring AI(6)——向量存储

向量数据库是一种特殊类型的数据库&#xff0c;在 AI 应用中发挥着至关重要的作用。 在向量数据库中&#xff0c;查询与传统关系型数据库不同。它们执行的是相似性搜索&#xff0c;而非精确匹配。当给定一个向量作为查询时&#xff0c;向量数据库会返回与该查询向量“相似”的…

【Matlab】最新版2025a发布,深色模式、Copilot编程助手上线!

文章目录 一、软件安装1.1 系统配置要求1.2 安装 二、新版功能探索2.1 界面图标和深色主题2.2 MATLAB Copilot AI助手2.3 绘图区升级2.4 simulink2.5 更多 延迟一个月&#xff0c;终于发布了&#x1f92d;。 一、软件安装 1.1 系统配置要求 现在的电脑都没问题&#xff0c;老…

uniapp,小程序中实现文本“展开/收起“功能的最佳实践

文章目录 示例需求分析实现思路代码实现1. HTML结构2. 数据管理3. 展开/收起逻辑4. CSS样式 优化技巧1. 性能优化2. 防止事件冒泡3. 列表更新处理 实际效果总结 在移动端应用开发中&#xff0c;文本内容的"展开/收起"功能是提升用户体验的常见设计。当列表项中包含大…

思维链框架:LLMChain,OpenAI,PromptTemplate

什么是思维链,怎么实现 目录 什么是思维链,怎么实现思维链(Chain of Thought)在代码中的实现方式1. 手动构建思维链提示2. 少样本思维链提示3. 自动思维链生成4. 思维链与工具使用结合5. 使用现有思维链框架:LLMChain,OpenAI,PromptTemplate思维链实现的关键要点思维链(C…

使用 QGIS 插件 OpenTopography DEM Downloader 下载高程数据(申请key教程)

使用 QGIS 插件 OpenTopography DEM Downloader 下载高程数据 目录 使用 QGIS 插件 OpenTopography DEM Downloader 下载高程数据&#x1f4cc; 简介&#x1f6e0; 插件安装方法&#x1f30d; 下载 DEM 数据步骤&#x1f511; 注册 OpenTopography 账号&#xff08;如使用 Cope…

计算机组成与体系结构:替换策略(MRU LRU PLRU LFU)

目录 &#x1f3b2; MRU&#xff08;最近最常使用&#xff09; &#x1fa9c; 操作流程&#xff1a; &#x1f3b2; LRU&#xff08;最近最少使用&#xff09; &#x1fa9c; 操作流程&#xff1a; 示例 &#x1f50d; Age Bits&#xff08;年龄位&#xff09; 核心思想…

websocket入门详解

入门websocket的基础应该掌握一下问题&#xff1a; 1、什么是握手&#xff1f; 2、什么是websocket&#xff1f; 3、websocket和http的区别&#xff0c;应用场景 4、html前端简单代码演示 5、springboot整合websocket使用 6、使用vueelementui打造简单聊天室 7、使用web…

(6)python开发经验

文章目录 1 QListWidget样式显示异常2 模块编码错误3 qtcreator开发pyqt编码错误 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;Qt开发 &#x1f448;&#x1f449;python开发 &#x1f448; 1 QListWidget样式显示异常 main.py import sys from PySide6.QtWi…

HPC软件使用之ANSYS Fluent

目录 一、软件介绍 二、脚本编写 2.1 jou文件 2.2 slurm脚本文件 三、作业提交及查看 四、案例演示 4.1 网格模型 4.2 jou脚本 4.3 slurm脚本 4.4 计算 4.5 结果查看 从本文开始&#xff0c;我们将介绍如何在超级计算机上使用科学计算、工程仿真计算软件开展计算&am…

YOLO11解决方案之距离计算探索

概述 Ultralytics提供了一系列的解决方案&#xff0c;利用YOLO11解决现实世界的问题&#xff0c;包括物体计数、模糊处理、热力图、安防系统、速度估计、物体追踪等多个方面的应用。 测量两个物体之间的间距被称为特定空间内的距离计算&#xff0c;YOLO11使用两个边界框的中心…

论文学习_Precise and Accurate Patch Presence Test for Binaries

摘要&#xff1a;打补丁是应对软件漏洞的主要手段&#xff0c;及时将补丁应用到所有受影响的软件上至关重要&#xff0c;然而这一点在实际中常常难以做到&#xff0c;研究背景。因此&#xff0c;准确检测安全补丁是否已被集成进软件发行版本的能力&#xff0c;对于防御者和攻击…

Ascend的aclgraph(九)AclConcreteGraph:e2e执行aclgraph

1回顾 前面的几章内容探讨了aclgraph运行过程中的涉及到的关键模块和技术。本章节将前面涉及到的模块串联起来&#xff0c;对aclgraph形成一个端到端的了解。 先给出端到端运行的代码&#xff0c;如下&#xff1a; import torch import torch_npu import torchair import log…

c++从入门到精通(四)--动态内存,模板与泛型编程

文章目录 动态内存直接管理内存Shared_ptr类Unique_ptrWeak_ptr动态数组allocator类文本查询程序 模板与泛型编程定义模板函数模板类模板模板参数成员模板控制实例化 模板实参推断重载与模板可变参数模板模板特例化 动态内存 c中动态内存的管理是通过new和delete运算符来实现的…

寻找两个正序数组的中位数 - 困难

************* Python topic: 4. 寻找两个正序数组的中位数 - 力扣&#xff08;LeetCode&#xff09; ************* Give the topic an inspection. Do the old topic will give you some new sparks. Before that, I do some really good craetive things about my logo. …

国产密码新时代!华测国密 SSL 证书解锁安全新高度

在数字安全被提升到国家战略高度的今天&#xff0c;国产密码算法成为筑牢网络安全防线的关键力量。华测国密SSL证书凭借其强大性能与贴心服务&#xff0c;为企业网络安全保驾护航&#xff0c;成为符合国家安全要求的不二之选&#xff01;​ 智能兼容&#xff0c;告别浏览器适配…

【金仓数据库征文】从云计算到区块链:金仓数据库的颠覆性创新之路

目录 一、引言 二、金仓数据库概述 2.1 金仓数据库的背景 2.2 核心技术特点 2.3 行业应用案例 三、金仓数据库的产品优化提案 3.1 性能优化 3.1.1 查询优化 3.1.2 索引优化 3.1.3 缓存优化 3.2 可扩展性优化 3.2.1 水平扩展与分区设计 3.2.2 负载均衡与读写分离 …

股指期货套期保值怎么操作?

股指期货套期保值就是企业或投资者通过持有与其现货市场头寸相反的期货合约&#xff0c;来对冲价格风险的一种方式。换句话说&#xff0c;就是你在股票市场上买了股票&#xff08;现货&#xff09;&#xff0c;担心股价下跌会亏钱&#xff0c;于是就在期货市场上卖出相应的股指…

基于IBM BAW的Case Management进行项目管理示例

说明&#xff1a;使用IBM BAW的难点是如何充分利用其现有功能根据实际业务需要进行设计&#xff0c;本文是示例教程&#xff0c;因CASE Manager使用非常简单&#xff0c;这里重点是说明如何基于CASE Manager进行项目管理&#xff0c;重点在方案设计思路上&#xff0c;其中涉及的…

黑马k8s(七)

1.Pod介绍 查看版本&#xff1a; 查看类型,这里加s跟不加s没啥区别&#xff0c;可加可不加 2.Pod基本配置 3.镜像拉去策略 本地没有这个镜像&#xff0c;策略是Never&#xff0c;启动失败 查看拉去策略&#xff1a; 更改拉去策略&#xff1a; 4.启动命令 运行的是nginx、busv…