【手撕数据结构】拿捏单链表

目录

  • 单链表介绍
  • 链表的初始化
  • 打印链表
  • 增加节点
    • 尾插
    • 头插
    • 再给定位置之后插入
    • 在给定位置之前插入
  • 删除节点
    • 尾删
    • 头删
    • 删除给定位置的节点
    • 删除给定位置之后的节点
  • 查找节点

单链表介绍

单链表也叫做无头单向非循环链表,链表也是一种线性结构。他在逻辑结构上一定连续,但是在物理结构上不一定连续(随机开辟空间),链表由一个或多个节点足够,每个节点由两部分组成,一个是存储的数据,一个是指向下一个节点的指针。

在这里插入图片描述

链表的初始化

typedef int SLTDataType;	//方便以后修改存储数据类型typedef struct SListNode
{SLTDataType val;struct SListNode* next;
}SLTNode;

打印链表

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

这里就直接依次打印元素即可,我们习惯不动头结点,所以创建一个变量存储头结点来遍历

增加节点

SLTNode* SLTBuyNode(SLTDataType x)
{SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror("malloc failed");exit(1);}else{newnode->val = x;newnode->next = NULL;}return newnode;
}

因为我们每次插入不管是头插还是尾插,都要创建一个新的节点,我们不妨封装一个函数专用

尾插

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{assert(pphead);//链表和顺序表不一样不是每次2倍申请空间,而是来一个数据申请一个空间SLTNode* newnode = SLTBuyNode(x);if (*pphead == NULL){*pphead = newnode;}else{SLTNode* pcur = *pphead;/*while (pcur){pcur = pcur->next;}pcur = newnode;*/	//这种写法虽然找到了插入节点的位置,但是无法修改上一个节点的next指针while (pcur->next){pcur = pcur->next;	//这里就找到了插入节点的位置}pcur->next = newnode;//并且修改了上一个节点的指针}
}

这里要做的就是两点,找到旧的尾节点,将旧的尾节点指针指向新的尾节点,然后将新的尾节点插入;

注:新结点创建的时候指针域就已经置空,所以尾插时不需要再将新结点的指针域置空。还有就是如果只有一个节点,那么就可以直接插入

头插

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{assert(pphead);SLTNode* newnode = SLTBuyNode(x);/*if (*pphead == NULL){*pphead = newnode;}else{SLTNode* pcur = *pphead;*pphead = newnode;(*pphead)->next = pcur;}*///优化版本newnode->next = *pphead;	//这里就巧妙用了SLTBuyNode函数创建节点时候将next指针设置为NULL特性,//反正newonde是在一个固定的位置插入,不用像尾插一样遍历,而插入节点我们现在只需要改变next指针//在改变原来的头结点之前把新头结点的next指向旧头结点,再更新头结点*pphead = newnode;

这里可以看优化版本,可以不处理链表为空的情况,因为我们总是在头结点的位置插入,只需要把新头节点的指针指向旧头结点,然后取代旧头结点

再给定位置之后插入

void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{assert(pos);SLTNode* newnode = SLTBuyNode(x);SLTNode* Next = pos->next;if (Next){pos->next = newnode;newnode->next = Next;}else{pos->next = newnode;}//优化版本//assert(pos);//SLTNode* newnode = SLTBuyNode(x);//newnode->next = pos->next;//pos->next = newnode;		//这种也可以同时处理一个节点和多个节点情况
}

这里也建议使用优化版本,不用看考虑只有一个节点的情况,插入就是把新节点的指针指向原来插入位置之后的节点,然后把指定位置的nxet指针指向2新节点。

在给定位置之前插入

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{assert(pphead);/*SLTNode* ret = SLTFind(pos,pos->val);assert(ret != NULL);*/assert(pos);	//外面用查找函数返回值即可SLTNode* newnode = SLTBuyNode(x);SLTNode* pcur = *pphead;if (pcur->next == NULL){SLTPushFront(pphead,x);}else{while (pcur->next != pos){pcur = pcur->next;}pcur->next = newnode;newnode->next = pos;}}

这里要注意的是,在给定位置之前插入数据,我们就要先找到给定位置之前的那个节点,将那个节点的next指针指向插入的节点,再将插入的节点next指针指向给定位置的节点即可。但是如果链表只有一个元素,那么我们永远找不到给定位置之前的节点,pcur->next!=pos恒成立,所以单独处理,一个节点之前插入,相当于头插,只需要调用头插函数即可

删除节点

尾删

void SLTPopBack(SLTNode** pphead)
{assert(pphead && *pphead);	//*pphead链表不能为空SLTNode* pcur = *pphead;if ((*pphead)->next == NULL)	//如果只有一个节点,则无需考虑next指针{free(*pphead);*pphead = NULL;}else{/*while (pcur->next){pcur = pcur->next;}free(pcur);pcur == NULL;*///上面不能写的原因是,虽然找到了尾节点并释放,但是前一个节点的next指针依然指向尾节点//导致下一次删除尾节点时,对一块释放的空间访问,这里把pucr尾节点设置NULL不代表上一个节点的值为NULL,他的值依然是这块被释放的空间while (pcur->next->next)//找到尾节点之前的节点{pcur = pcur->next;}SLTNode* del = pcur->next;		//因为循环只能找到尾节点之前的节点,释放尾节点之前,把尾节点存起来free(del);del = NULL;pcur->next = NULL;}
}

这里要注意,我们先找到尾节点并释放那块内存空间,但是上一个节点的next指针也要记得设置NULL,不然他依然指向这块被释放的空间.

头删

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

头删与尾删的区别是不需要考虑被删除节点的next指针,因为单链表是向后遍历的,头删的next指针并不会影响新的链表的遍历,所以我们只需找到旧头节点的下一个节点作为新的头节点,把原来的头节点释放即可

删除给定位置的节点

void SLTErase(SLTNode** pphead, SLTNode* pos)
{assert(pphead && *pphead);	//*pphead不为空链表assert(pos);if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else if (*pphead == pos){*pphead = (*pphead)->next;free(pos);pos = NULL;}else{SLTNode* Next = pos->next;SLTNode* pcur = *pphead;while (pcur->next != pos){pcur = pcur->next;}free(pos);pos = NULL;pcur->next = Next;}//优化版本//if (pos == *pphead)//{//	SLTPopFront(pphead);	//头删既可以解决只有一个节点,又可以解决指定节点与头结点相同//}//else//{//	SLTNode* prev = *pphead;//	while (prev->next != pos)	//这里不能处理头节点与指点节点相同情况//	{//		prev = prev->next;//	}//	prev pos pos->next//	prev->next = pos->next;//	free(pos);//	pos = NULL;//}}

要删除给定位置的节点之前要先判断这个节点是否在链表中存在(SLTFind)方法 提供pos参数,这里通常只需要找到给定位置之前的节点,然后将其的next指针指向给定位置之后的节点即可.但是有两种情况需要单独处理,第一种情况就是链表只有一个节点时,只需要直接头删即可,第二种情况就是链表有多个节点,我们要删除头结点,这样的我们永远找不到给定位置之前的节点,pcur->next != pos恒成立,这种情况也可以通过头删函数解决

删除给定位置之后的节点

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

注意:不能像注释那样写的原因:在这里插入图片描述

查找节点

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

只需要从头节点开始遍历查找即可,如果找到了就返回该节点,如果到NULL,返回null;

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

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

相关文章

(Qt) 文件读写基础

文章目录 🗂️前言📄ref📄访问标记🗃️enum 标记 🗂️Code📄demo📄分点讲解🗃️继承体系🗃️打开/关闭🗃️写🗃️读 🗂️END&#x1f…

从零开始:构建基于深度学习的实时跌倒检测系统(UI界面+YOLO代码+数据集)

注意看文末的结局与声明 一、引言 1. 项目背景与动机 在老年人和高危职业环境中,跌倒是一种常见的事故,可能导致严重的伤害甚至致命。实时跌倒检测系统可以及时发现并报警,提供紧急救助。通过深度学习技术,可以提高跌倒检测的准…

Android14 - 前台Service、图片选择器 、OpenJDK 17、其他适配

前台服务 1. 指定前台服务类型 以 Android 14(API 级别 34)或更高版本为目标平台的应用,需要为应用中的每项前台服务指定服务类型,因为系统需要特定类型的前台服务满足特定用例。具体介绍如下: 在Android 10 在 <service> 元素内引入了 android:foregroundServiceT…

Java并发的笔记

打算记录自己的学习情况&#xff0c;尽量不摆烂&#xff0c;另外一件事要有始有终&#xff0c;要弄完 如果多个线程处理同一个变量&#xff0c;读跟写都保证不了 2024.7.22》》》》》》》》》》》》 2.1.1volatile的实现原理 volatile不会引起线程上下文的切换和调度 一致性更…

pycharm+pyqt6配置

1、pip install pyqt6 pyqt6-toools 2、pycharm配置 配置&#xff1a;designer Program&#xff1a;&#xff1a;D:\Python39\Lib\site-packages\qt6_applications\Qt\bin\designer.exe Working directory: $ProjectFileDir$ 配置&#xff1a;pyuic6.exe Program&#xff1a…

调度子系统在特定时间执行

时序逻辑调度器设计模式允许您安排Simulink子系统在指定时间执行。以下模型说明了这种设计模式。 时序逻辑调度器图表包含以下逻辑&#xff1a; 时序逻辑调度器的关键行为 时序逻辑调度器图表包含两个状态&#xff0c;它们以不同的速率调度函数调用子系统A1、A2和A3的执行&…

DVWA靶场超(详细教程)--跨站攻击(XSS+CSRF)

一、XSS 反射型xss 打开dvwa的Reflected Cross Site Scripting (XSS) &#xff08;1&#xff09;low等级 查看页面源码&#xff08;ctrlu&#xff09;该界面有提交按钮和输入框 在输入框随便输入点字符&#xff0c;点击提交 可以看见输入的helloword嵌入到界面中。 View sou…

PS启动提示Adobe Creative Cloud丢失或损坏。您可以尝试修复来解决这个问题,如何解决

一般为找到这个路径下C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS的Adobe Desktop Service.exe文件。如果不在C盘也可以直接搜索其他盘找到此文件。 直接删除此文件即可解决&#xff0c;如果删除不了可以进任务管理器先结束进程再删除。鼠标右键结束任…

DolphinScheduler安装教程

DolphinScheduler安装教程 前期准备工作 jdk 1.8mysql 5zookeeper 3.4.6hadoop 2.6psmisc yum -y install psmisc 解压安装包 # 将安装包apache-dolphinscheduler-2.0.8-bin.tar.gz放置/opt/download目录下 # 解压缩 tar -zxvf apache-dolphinscheduler-2.0.8-bin.tar.gz -C …

看准JS逆向案例:webpack逆向解析

&#x1f50d; 逆向思路与步骤 抓包分析与参数定位 首先&#xff0c;我们通过抓包工具对看准网的请求进行分析。 发现请求中包含加密的参数b和kiv。 为了分析这些加密参数&#xff0c;我们需要进一步定位JS加密代码的位置。 扣取JS加密代码 定位到JS代码中的加密实现后&a…

学习java第一百三十八天

Bean的作用域 1、singleton&#xff1a;单例&#xff0c;Spring中的bean默认都是单例的。 2、prototype&#xff1a;每次请求都会创建一个新的bean实例。 3、request&#xff1a;每一次HTTP请求都会产生一个新的bean&#xff0c;该bean仅在当前HTTP request内有效。 4、session…

PGSQL学习-基础表结构

1 访问数据库 创建好数据库后&#xff0c;你可以有三种方式访问数据库 运行PostgreSQL的交互式终端程序&#xff0c;它被称为psql&#xff0c; 它允许你交互地输入、编辑和执行SQL命令。 使用一种已有的图形化前端工具&#xff0c;比如pgAdmin或者带ODBC或JDBC支持的办公套件…

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

题目描述&#xff1a; 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&…

GPU驱动、CUDA 、cuDNN 和CUDA Toolkit之间的关系(深度学习小白必懂)

GPU驱动、CUDA、cuDNN和CUDA Toolkit之间有着紧密的关系&#xff0c;它们共同构成了一个完整的GPU编程和深度学习开发环境。 在最初配置anaconda环境时一直搞不明白它们之间的关系。所以根据自己的理解&#xff0c;通俗详细解释它们各自的角色和相互间的关系&#xff0c;并且列…

前端面试 vue 接口权限控制

接口权限目前一般采用jwt的形式来验证&#xff0c;没有通过的话一般返回401&#xff0c;跳转到登录页面重新进行登录 对于 jwt的理解 &#xff08;前端接口权限的控制主要通过接口权限配置和JWT&#xff08;‌Json Web Token&#xff09;‌技术来实现。‌ 首先&#xff0c;‌…

前端设计模式面试题汇总

面试题 1. 简述对网站重构的理解&#xff1f; 参考回答&#xff1a; 网站重构&#xff1a;在不改变外部行为的前提下&#xff0c;简化结构、添加可读性&#xff0c;而在网站前端保持一致的行为。也就是说是在不改变UI的情况下&#xff0c;对网站进行优化&#xff0c; 在扩展的…

红人点集登录逆向+接口逆向:SHA256算法和Webpack反爬

&#x1f50d; 引言 红人点集采取了一系列加密和限制措施&#xff0c;主要是对于参数加密和登录token加密。今天利用Python与JavaScript逆向工程技术&#xff0c;实现逆向登录然后请求接口获取数据。 &#x1f50d; 思路与步骤详解 &#x1f527; 解密登录接口参数&#xf…

【k8s故障处理篇】calico-kube-controllers状态为“ImagePullBackOff”解决办法

【k8s故障处理篇】calico-kube-controllers状态为“ImagePullBackOff”解决办法 一、环境介绍1.1 本次环境规划1.2 kubernetes简介1.3 kubernetes特点二、本次实践介绍2.1 本次实践介绍2.2 报错场景三、查看报错日志3.1 查看pod描述信息3.2 查看pod日志四、报错分析五、故障处理…

机器学习·概率论基础

概率论 概率基础 这部分太简单&#xff0c;直接略过 条件概率 独立性 独立事件A和B的交集如下 非独立事件 非独立事件A和B的交集如下 贝叶斯定理 先验 事件 后验 在概率论和统计学中&#xff0c;先验概率和后验概率是贝叶斯统计的核心概念 简单来说后验概率就是结合了先验概…

如何将mp4格式的视频压缩更小 mp4格式视频怎么压缩最小 工具软件分享

在数字化时代&#xff0c;视频内容成为信息传播的重要载体。然而&#xff0c;高清晰度的视频往往意味着较大的文件体积&#xff0c;这给存储和分享带来了一定的困扰。MP4格式作为目前最流行的视频格式之一&#xff0c;其压缩方法尤为重要。下面&#xff0c;我将为大家详细介绍如…