链表(2) ---- 完整版

目录

  • 一 . 前言
  • 二 . 头文件声明
  • 三 . 代码思绪
    • 1. 查找函数的实现
    • 2. 在指定位置之前插入
    • 3. 在指定位置之后插入
    • 4. 删除指定位置的结点
    • 5. 删除指定位置之后的结点
    • 6. 销毁链表
  • 四 . 完整代码
  • 五 . 总结


正文开始

一 . 前言

在这里插入图片描述

补充说明:
1、链式机构在逻辑上是连续的,在物理结构上不⼀定连续
2、节点⼀般是从堆上申请的
3、从堆上申请来的空间,是按照⼀定策略分配出来的,每次申请的空间可能连续,可能不连续

本篇旨在实现链表的剩下的方法

//查找 
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);
//在指定位置之前插⼊数据 
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);
//删除pos节点 
void SLTErase(SLTNode** pphead, SLTNode* pos);
//在指定位置之后插⼊数据 
void SLTInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos之后的节点 
void SLTEraseAfter(SLTNode* pos);
//销毁链表 
void SListDesTroy(SLTNode** pphead);


二 . 头文件声明

接上文, 在头文件中先声明以下函数, 然后在.c文件中进行实现
见3实现详情

#pragma once

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

typedef int DataType;

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

void SLTPrint(SLTNode* phead);

//尾插
void SLTPushBack(SLTNode** pphead, DataType x);
//头插
void SLTPushFront(SLTNode** pphead, DataType x);
//尾删
void SLTPopBack(SLTNode** pphead);
//头删
void SLTPopFront(SLTNode** pphead);

//查找
SLTNode* SLTFind(SLTNode* phead, DataType x);

//在指定位置之前插入数
void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType x);
//在指定位置之后插入数据
void SLTInsertAfter(SLTNode* pos, DataType x);

//删除指定位置结点
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除指定位置之后的结点
void SLTEraseAfter(SLTNode* pos);

//销毁链表
void SLTDestory(SLTNode** pphead);


三 . 代码思绪

1. 查找函数的实现

查找不需要改变头结点所指向的地址, 所以不需要取地址, 直接接收形参和要查找的数据, 临时变量pcur用来遍历链表, 让发现所要查找的数据与之遍历的结点所匹配时, 停止遍历返回此节点, 没有找到返回空.

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

2. 在指定位置之前插入

需要断言头结点和指定位置都不能为NULL, 调用函数创建新的结点, 首先需要先找到指定位置的前一个结点, 找到之后, 先让新节点的指针域指向pos结点, 然后再让前一个结点的指针域指向新节点.
但是这里需要注意, 如果pos为头结点, 则prev会对NULL进行解引用, 程序报错, 所以需要单独判断, 让pos为头结点时可以使用头插, 也可以先让新节点的下一个位置指向头结点, 然后再改变头指针指向新节点.

注: 创建新节点的函数在上篇

void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	
	SLTNode* newnode = SLTBuyNode(x);

	if (pos == *pphead)
	{
		newnode->next = *pphead;
		*pphead = newnode;
		//SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

测试一下:

	SLTNode* plist = NULL;
	SLTPushBack(&plist, 100);
	SLTPrint(plist);
	SLTPushBack(&plist, 88);
	SLTPrint(plist);

	SLTPushFront(&plist,99);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);

	SLTPopFront(&plist);
	SLTPrint(plist);

	SLTNode* find =  SLTFind(plist, 100);
	SLTPrint(find);

	SLTInsert(&plist,find,200);
	SLTPrint(plist);

执行结果:

在这里插入图片描述

只能说:

打印代码在这里:

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

3. 在指定位置之后插入

不需要传递头指针了, 因为用不到, 可以直接通过pos->next找到要插入的位置, 断言pos不能为NULL, 创建新结点, 先让新节点的指针域指向pos的下一个结点, 之后让pos的指针域指向新的结点.

void SLTInsertAfter(SLTNode* pos, DataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

测试一下:

	SLTNode* plist = NULL;
	SLTPushBack(&plist, 100);
	SLTPrint(plist);
	SLTPushBack(&plist, 88);
	SLTPrint(plist);

	SLTPushFront(&plist,99);
	SLTPrint(plist);

	SLTPopBack(&plist);
	SLTPrint(plist);

	SLTPopFront(&plist);
	SLTPrint(plist);

	SLTNode* find =  SLTFind(plist, 100);
	SLTPrint(find);

	SLTInsert(&plist,find,200);
	SLTPrint(plist);

	SLTInsertAfter(find,300);
	SLTPrint(plist);

代码结果:
在这里插入图片描述

没毛病!

4. 删除指定位置的结点

首先还是需要断言, 不可以为空链表空地址, 不然你让我删什么, 先找到要删除位置的前一个结点, 因为要把它的指针域置为NULL, 否则删除pos节点后 它会成为野指针,定义prev, 找到前一个结点之后,让prev指向pos之后的那个结点, 跳过pos结点, 然后将pos结点释放掉,并且置为NULL.
注意如果要删除头指针的话这里遍历prev->next一样会非法访问NULL,所以需要单独判断, 直接调用头删方法即可, 思路是一样的.

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(pphead && *pphead);

	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

测试一下:

	SLTErase(&plist, find);
	SLTPrint(plist);

代码结果:
此时100已被删除

在这里插入图片描述
没毛病!

5. 删除指定位置之后的结点

这里也不需要传递头结点, 因为用不到, 需要断言pos和要删除的结点是否存在, 定义del记录一下要删除的结点, 之后直接让pos的指针域指向下下一个结点, 而此时要删除的结点已经保存在del中, 所以直接free掉,最后置为NULL.

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

测试一下:


	SLTEraseAfter(find);
	SLTPrint(plist);

代码运行结果:

在这里插入图片描述
只能说:

6. 销毁链表

为空的话就需要销毁了, 所以先断言一下是否为NULL, 然后定义临时变量遍历链表, 存储好下一个待遍历的位置之后free掉pcur即可, 最后不要忘了将头指针也置为空

void SLTDestory(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

四 . 完整代码

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

void SLTInsert(SLTNode** pphead, SLTNode* pos, DataType x)
{
	assert(pphead && *pphead);
	assert(pos);
	
	SLTNode* newnode = SLTBuyNode(x);

	if (pos == *pphead)
	{
		newnode->next = *pphead;
		*pphead = newnode;
		//SLTPushFront(pphead,x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		newnode->next = pos;
		prev->next = newnode;
	}
}

void SLTInsertAfter(SLTNode* pos, DataType x)
{
	assert(pos);
	SLTNode* newnode = SLTBuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pos);
	assert(pphead && *pphead);

	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
		pos = NULL;
	}
}

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos && pos->next);
	SLTNode* del = pos->next;
	pos->next = del->next;
	free(del);
	del = NULL;
}

void SLTDestory(SLTNode** pphead)
{
	assert(pphead && *pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

五 . 总结

链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据元素和一个指向下一个节点的指针。链表的优点是插入和删除操作的时间复杂度为O(1),而不受链表长度的影响。然而,链表的缺点是访问元素的时间复杂度为O(n),因为需要遍历整个链表。

链表常见的类型有单链表、双向链表和循环链表。

  • 单链表:每个节点只有一个指向下一个节点的指针。
  • 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
  • 循环链表:链表中最后一个节点的指针指向链表中的第一个节点。

链表的操作包括插入、删除和查找。

  • 插入操作:将一个新的节点插入到链表的某个位置,需要修改前后节点的指针。
  • 删除操作:删除链表中的某个节点,需要修改前后节点的指针。
  • 查找操作:根据给定的值查找链表中的节点。

在实际应用中,链表常用于实现栈、队列和图等数据结构。此外,链表还有一种特殊的应用——LRU缓存淘汰算法,用于解决缓存容量有限的情况下,如何选择合适的缓存进行淘汰的问题。

总结来说,链表是一种重要的数据结构,具有插入和删除操作效率高的优点,适用于需要频繁插入和删除操作的场景。但是在访问元素方面比较慢,需要遍历整个链表。了解链表的特点和操作有助于程序员更好地理解和应用链表。


完, 本文分享就到这里, 感谢大家的支持 关注 点赞 收藏 !!!

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法】前缀和

快乐的流畅&#xff1a;个人主页 个人专栏&#xff1a;《算法神殿》《数据结构世界》《进击的C》 远方有一堆篝火&#xff0c;在为久候之人燃烧&#xff01; 文章目录 引言一、一维前缀和二、二维前缀和三、寻找数值的中心下标四、除自身以外数组的乘积五、和为k的子数组六、和…

【Github】sync fork后,意外关闭之前提交分支的pr申请 + 找回被关闭的pr请求分支中的文件

【Github】sync fork后&#xff0c;意外关闭之前提交分支的pr申请 找回被关闭的pr请求分支中的文件 写在最前面原因解析提交pr&#xff0c;pr是什么&#xff1f;rebase 或者 merge 命令 找到分支中被删除的文件找到被关闭的提交请求pr方法1&#xff1a;在公共仓库被关闭的pr中…

LeetCode in Python 69. Sqrt(x) (x的平方根)

求x的平方根&#xff0c;第一想法可能是遍历0&#xff5e;x&#xff0c;求其平方&#xff0c;找到或且但其时间复杂度为O(n)&#xff0c;或是想到遍历0&#xff5e;M即可&#xff0c;其中M x // 2&#xff0c;将时间复杂度降至O()。本文利用二分思想&#xff0c;给出一种时间复…

博睿数据亮相GOPS全球运维大会,Bonree ONE 2024春季正式版发布!

2024年4月25日&#xff0c;博睿数据 Bonree ONE 2024 春季正式版焕新发布。同时&#xff0c;博睿数据AIOps首席专家兼产品总监贺安辉携核心产品新一代一体化智能可观测平台 Bonree ONE 亮相第二十二届 GOPS 全球运维大会深圳站。 Bonree ONE 2024 春季版产品重点升级数据采集、…

网上打印资料多少钱一张?网上打印价格是多少?

在数字化时代&#xff0c;网上打印服务正逐渐成为一种便捷、高效的打印解决方案。对于许多需要打印资料的用户来说&#xff0c;了解网上打印的价格和服务质量至关重要。那么&#xff0c;网上打印资料到底多少钱一张&#xff1f;网上打印价格又是如何呢&#xff1f;今天&#xf…

视频号下载小程序:轻松获取视频号视频

在数字化时代&#xff0c;短视频已成为人们日常生活中不可或缺的一部分。为了满足用户随时随地观看视频的需求&#xff0c;视频号小程序应运而生。本文将详细介绍视频号小程序的下载方法、功能特点以及使用技巧&#xff0c;帮助您更好地享受短视频带来的乐趣。 一、视频号小程…

C++ 之 string类 详细讲解

喜欢的人有点难追怎么办 那就直接拉黑 七个女生在一起是七仙女&#xff0c;那七个男生在一起是什么&#xff1f; 葫芦七兄弟 目录 一、为什么要学习string类 二、标准库中的string类 1.string类 2.string类的常用接口说明 2.1 string类对象的常见构造 2.2 string类对…

Vivado-OOC

OOC⇒Out-of-Context 在Vivado中&#xff0c;对于顶层设计&#xff0c;vivado使用自顶向下的全局&#xff08;global&#xff09;综合&#xff0c;将顶层文件下的所有模块都进行综合&#xff0c;但是在实际设计过程中&#xff0c;顶层设计会被多次修改和综合&#xff0c;但是有…

AI语音侵权第一案:配音演员获赔25万元,如何保护你的声音资产?

会议之眼 快讯 近日&#xff0c;北京互联网法院对全国首例AI声音侵权案作出一审宣判&#xff0c;引发了社会对AI技术与个人权益保护关系的广泛讨论。 原告殷某&#xff0c;一名配音师&#xff0c;发现自己的声音被AI化后在“魔音工坊”APP上出售&#xff0c;遂将运营主体等五…

Linux的学习之路:20、进程信号(2)

摘要 本章讲一下进程信号的阻塞信号和捕捉信号和可重入函数 目录 摘要 一、阻塞信号 1、阻塞信号 2、信号集操作函数 二、捕捉信号 1、内核如何实现信号的捕捉 2、代码实演 三、可重入函数 一、阻塞信号 1、阻塞信号 实际执行信号的处理动作称为信号递达(Delivery) …

MyBatis源码之前言—JDBC编码存在的问题和Mybatis的介绍

MyBatis源码之前言—JDBC编码存在的问题和Mybatis的介绍 为了方便操作&#xff0c;我们在sjdwz_test数据库下建立一张表&#xff1a; CREATE TABLE t_student (id bigint(20) NOT NULL AUTO_INCREMENT COMMENT 主键,name varchar(255) DEFAULT NULL COMMENT 名字,age int(255…

Web端Webrtc,SIP,RTSP/RTMP,硬件端,MCU/SFU融合视频会议系统方案分析

Web端视频融合&#xff0c;会议互通已经是视频会议应用的大趋势&#xff0c;一是目前企业有大量的老视频会议硬件设&#xff0c;二新业务又需要Web端支持视频会议监控直播需求&#xff0c;迫切需要一个融合对接的方案&#xff0c;即能把老的设备用起来&#xff0c;又能对接新的…

【每日刷题】Day22

【每日刷题】Day22 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1669. 合并两个链表 - 力扣&#xff08;LeetCode&#xff09; 2. 11. 盛最多水的容器 - 力扣&#…

分类算法——ROC曲线与AUC指标(九)

知道TPR与FPR TPRTP/(TP FN) 所有真实类别为1的样本中&#xff0c;预测类别为1的比例 FPR FP/(FP TN) 所有真实类别为0的样本中&#xff0c;预测类别为1的比例 ROC曲线 ROC曲线的横轴就是FPRate&#xff0c;纵轴就是TPRate&#xff0c;当二者相等时&#xff0c;表示的意义…

Linux 内核设备树 ranges属性

今天有人问了我一下ranges属性&#xff0c;找了相关资料确认后&#xff0c;记录一下&#xff1a; 参考资料链接&#xff1a;让你完全理解linux内核设备树ranges属性地址转换 - vkang - 博客园 (cnblogs.com) ranges属性定义如下&#xff1a; ranges < local_address pa…

webpack面试题(持续汇总ing。。。)

webpack的编译过程 初始化 此阶段&#xff0c;webpack会将CLI参数、配置文件、默认配置进行融合&#xff0c;形成一个最终的配置对象。对配置的处理过程是依托一个第三方库 yargs 完成的。此阶段相对比较简单&#xff0c;主要是为接下来的编译阶段做必要的准备目前&#xff0c;…

三数之和 ---- 双指针

题目链接 题目: 分析: 解法一: 暴力解法, 将所有的三元组都算出来看是否为0, 题目要求去重操作, 所以我们可以使用set去重解法二: 因为我们知道当计算两数之和时, 我们使用的方法是将数组排序,然后利用"双指针"那么同理, 计算三个数之和: 1. 排序2. 固定一个数a, …

数据库管理-第176期 浅析代码团队建设(20240425)

数据库管理176期 2024-04-25 数据库管理-第176期 浅析代码团队建设&#xff08;20240425&#xff09;1 国内现状2 需求管控3 竞争与迭代总结 数据库管理-第176期 浅析代码团队建设&#xff08;20240425&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09…

安卓Activity的setContentView()流程分析

目录 前言一、Activity的视图加载过程1.1 视图结构1.2 流程分析1.2.1 Activity.java -->setContentView()1.2.2 Activity.java -->getWindow()1.2.3 PhoneWindow.java -->setContentView()1.2.4 PhoneWindow.java --->installDecor()1.2.4.1 PhoneWindow.java ---&…

Yolov5 export.py实现onnx模型的导出

查了很多资料&#xff0c;很多用python代码写的&#xff0c;只需要这个库那个库的&#xff0c;最后都没成功。 不如直接使用Yolov5里面的 export.py实现模型的转换。 一&#xff1a;安装依赖 因为yolov5里面的requirments.txt是将这些转换模型的都注释掉了 所以需要解除注释…