数据结构-----链表

目录

1.顺序表经典算法

(1)移除元素

(2)合并数组

2.链表的创建

(1)准备工作

(2)建结构体

(3)链表打印

(4)尾插数据

(5)头插数据

(6)尾删数据

(7)头部删除

(8)查找数据

(9)在指定位置之前插入数据

(10)在指定位置之后插入数据

(11)删除指定位置的节点

(12)删除指定位置之后的节点


1.顺序表经典算法

(1)移除元素

我们这里使用双指针的移动解决这个问题:

(1)指针dest和src, 例如3 2 2 3,我们的val是3,当开始的时候,src和dest都指向3,正好是我们想要删除的元素,我们的dest不变,src++,src此时指向的2不是我们想要删除的元素,就把2挪到前一位,覆盖掉3,这个顺序表里面的头删很相似,按照这个,当src执行最后一个元素的时候等于val,再次执行加加就结束了,此时我们的dest正好指在第二个2的位置,恰好是我们想要的返回值,直接返回就可以了。

(2)合并数组

我们选择的是使用l1,l2,l3,分别指向第一个数组的末尾,第二个数组的末尾,第一个数组的最后,我们的第一个数组的长度是两个数组的长度之和,l3指向的是没有数字的空位置,让两个数组里面的元素进行比较,大的就从第一个数组的后面开始布置,知道一个数组遍历完成,当第一个数组遍历完成但是第二个数组没有遍历完成的时候,就说明第一个数组里面的元素全部大于第二个数组,我们就需要使用循环直接把第二个数组里面的元素挪到第一个数组里面,如果第二个数组遍历完成,但是第一个数组没有遍历完成,这个时候数组的顺序就是按照大小进行排列的,我们不需要进行任何处理(读者可自行尝试);

2.链表的创建

中间头部插入数据效率低下,增容时浪费空间,效率低下,链表可以解决这个问题:

(1)准备工作

创建三个文件,一个头文件----slist.h文件,两个源文件------slist.c------test.c文件,头文件还是主要负责相关的函数的声明以及结构体的创建,一个源文件slist.c是链表的相关函数的实现,另外的一个test.c就是进行函数的功能的测试:

(2)建结构体

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int sldatatype;
typedef struct slistnode
{sldatatype data;struct slistnode* next;
}slnode;

创建类型是struct slistnode的结构体,并且重新命名为slnode类型,这样会方便我们后续变量的创建,结构体里面有data就是存放的数据,另外的一个就是我们的next指针,因为我们的链表不是连续的,所以既需要变量存放数据,也需要定义一个next指针找到下一个数据;

(3)链表打印

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

这个是链表的源文件里面的一个链表的打印函数的实现,因为链表不是连续的,我们无法利用之前的那种加加减减操作依次打印,我们需要移动指针,逐个进行打印;结合下面的测试代码,我们定义了一个plist指针指向我们的第一个节点,plist作为实际参数,phead作为形式参数接受,这个时候phead也是指向了第一个节点,重新定义了一个指针pcur指向第一个节点,通过pcur=pcur->next使得pcur不断地指向下一个结点,循环进行打印,失恋了链表的遍历。

(4)尾插数据

先找到尾部的节点,让尾部的节点和我们的新的节点连接起来:我们首先要新建一个节点,因为进行头插函数设计的时候也会涉及到这个节点的新建,因此我们把定义新的节点,封装成为一个函数,我们进行头插尾插的时候直接对函数进行调用就可以了;

尾部插入数据,首先要判断我们这个一直的链表是否有数据,没有就直接指向新建的节点;有的话进行循环找到尾部的节点,实现链接;因此尾插的时候需要划分为两种情况,否则空的话解引用就会报错,下面的是实参的三种形式;

在我们的测试函数里面,我们首先定义了一个空的链表,plist是指向第一个节点的指针,我们置为空指针,说明初始的情况下这个链表里面是没有元素的,我们如果在形参里使用一级指针进行接收,就会发现,我们给phead传进去的最后一个数字是4,我们的phead的数据始终在变化,但是并没有同步到plist里面去,这个就可以说明我们的传递的是值,虽然plist就是一个指针,但是我们应该传递这个一级指针plist的地址,这样才可以让形参的变化同步到实参;

我们传递了一级指针plist的地址,在形参部分就应该使用二级指针进行接收,跟就实参的三种形式, 我们对于尾插代码里面相应的部分进行修改;

当我们使用的二级指针进行接收的时候,我们就可以发现,相残和实参的变化就会同步了:phead修改的时候,plist改变量也会被同步进行。

(5)头插数据

和尾部插入有些许类似,我们首先要新建一个节点,我们要想实现关联,我们首先就要让这个新建的节点只想我们的头节点,然后再让我们这个新建的节点成为头节点;这里只需要修改第一个指针的指向,就可以完成链表的头插数据,不需要让剩余的节点向后移动;

(6)尾删数据

尾删数据,我们首先就要考虑先找到最后的一个数据节点,让后把这个空间给释放掉,然后设置为空指针,前提是我们的节点不能是空的,否则我们就没有可以删除的数据了;之前我们的插入*pphead(指向第一个节点的指针)可以是空的,我们只需要对二级指针**pphead进行断言就可以了,因为就算没有数据,我们自己也是可以插入数据的,但是这个地方我们必须同时对于一级指针和二级指针进行断言,因为如果没有数据,我们可以进行插入,但是无法进行删除;

这个时候我们比如有1 2 3 4这四个元素,4被删除之后,我们的倒数第二个元素原本是指向的4,现在这个4被我们给释放掉了,这个节点处的指针就变成了野指针,因此我们也要把这个倒数第二个节点的next指针指向空才可以;

但是想象一下一种极端的情况,如果本来就是空的话,我们释放掉之后,这块空间就消失了,就不会存在pre前一个指针变成野指针的情况了;

(7)头部删除

首先我们要考虑一个问题:如果是先释放掉,然后把第二个节点变成头节点,这个时候我们必须要意识到,当我们把空间释放掉之后,我们第一个结点的next指针里面的是第二个节点的地址,我们把它释放掉之后,我们是无法找到第二个元素的,我们因此先要把第二个元素的地址给存起来,让后进行释放空间,最后再把我们的第二个结点的地址赋值给我们的*pphead(指向第一个节点的指针);

(8)查找数据

我们这里在一般的情况下都会定义一个pcur=phead,两者的效果是一样的,相当于pcur就是phead的一个复制品,我们为社么要这么进行定义呢?因为如果我们没有定义pcur这个指针,而是直接使用phead指针,随着指针的移动,当我们遍历完成之后,如果我们想要再次进行处理的时候,就不能找到头部节点的位置了,但是如果我们定义了一个pcur指针,无论我们的pcur怎样进行移动,我们的phead始终是位置不变的,我们始终是能够找到这个链表的头位置的,对于这个思想,我们应该逐步地理解并且学会运用;

我们可以给一个返回值方便我们进行判断:if语句是判断是否是我们想要的数据,如果不是的话就会继续向下走,如此进行下去,除了while循环之后还是没有找到的话,就说明我们想要查找的数据在链表里面不存在,我们返回的是空指针;找到的话我们直接返回pcur就可以了,因为我们的pcur是一个slnode*类型的指针,那么用来进行接收返回值的find就应该是相同的类型;

(9)在指定位置之前插入数据

这个时候,我们就需要三个参数,我们的指针(二级指针),我们的插入的位置pos,以及我们想要插入的数据(sldatatype x),先要进行断言,我们的pos是一定存在的,所以*pphead不可以是空指针;同样,我们使用pre同样表示的指向第一个节点的指针,如果我们的pos就是第一个结点的话,我们的循环条件是pre->next!=pos,指针他就会不停的向后面进行移动,我们因此要进行判断pos(我们想要进行查找的位置)是否和*pphead相等,如果相等的话,就说明首个节点的位置就是我们的pos位置,我们要在这个位置之前插入数据,就是进行的头部插入,我们直接进行调用前面定义的头插函数就可以了;否则的话就说明pos位置不是我们的第一个加点位置,我们逐个进行遍历,直到找到我们的pos位置即可;找到这个pos位置之后,我们直接让pre(指定位置的前面的一个节点)指向我们的新的节点,让我们的新的节点指向pre->next这个位置的节点,这样就可以在我们的指定位置前面把新的节点插入进去了;

(10)在指定位置之后插入数据

下面就是一个简单的只有4个数据的链表,我们只是为了说明问题:我们在指定的位置的后面插入数据,可有2种方案,一种就是先1后2,还有就是先2后1,先2后1才是正确的,先1后2是错误的(如果是先1后2的话,我们的pos->next指针就已经指向了新的节点,然后是newnode->next指向我们的pose->next,这个时候的pos->next就已经变化了,不是我们想要的了,这个不就是自己指向自己嘛),所以只能是先2后1;

我们这个地方只需要两个参数,不需要头节点,我们前面的指定位置前面插入数据,我们找到了指定的位置,但是我们没有办法知道前面的一个,因此我们定义了一个副本pre进行从头进行遍历,但是这个地方我们在指定位置的后面插入数据,我们的pos找到了以后,我们的pos->next就直接找到了,不需要用到头节点,因此我们只用涉及两个参数就可以了;

(11)删除指定位置的节点

只有一个节点,直接进行头删函数的调用就可以了;否则让指定位置前面的节点链接指定位置后面的节点;最后释放指定位置节点,置为空指针;

(12)删除指定位置之后的节点

pos和pos后面的一个节点必须要有数据,进行断言,使用del作为副本存储指定的位置,链接pos->next和del->next;释放之后置空;

(13)销毁链表

一个一个地进行销毁节点,最后把头节点销毁,我们在销毁单个节点之前要存下一个节点的位置

3.完整代码

(1)slist.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int sldatatype;
typedef struct slistnode
{sldatatype data;struct slistnode* next;
}slnode;void slnodeprint(slnode* phead);void slpushback(slnode** pphead, sldatatype x);void slpushfront(slnode** pphead, sldatatype x);void slpopback(slnode** pphead);void slpopfront(slnode** pphead);slnode* slfind(slnode* phead, sldatatype x);slnode* slinsert(slnode** pphead, slnode* pos, sldatatype x);slnode* slinsertafter(slnode* pos, sldatatype x);void slerase(slnode** pphead, slnode* pos);void sleraseafter(slnode* pos);void destory(slnode** pphead);

(2)slist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
void slnodeprint(slnode* phead)
{slnode* pcur = phead;while (pcur){printf("%d->", pcur->data);pcur = pcur->next;}printf("NULL\n");
}
slnode* buynode(sldatatype x)
{slnode* newnode = (slnode*)malloc(sizeof(slnode));if (newnode == NULL){perror("malloc");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}
尾部插入数据
//void slpushback(slnode** pphead, sldatatype x)
//{
//	assert(pphead);
//	slnode* newnode = buynode(x);
//	if (*pphead == NULL)
//	{
//		*pphead = newnode;
//	}
//	else
//	{
//		//找到尾部的节点,使之只想我们插入的元素
//		slnode* ptail = *pphead;
//		while (ptail->next)
//		{
//			ptail = ptail->next;
//		}
//		//跳出循环之后ptail指向的就是尾部节点
//		ptail->next = newnode;
//	}
//}//链表的尾插数据
void slpushback(slnode** pphead, sldatatype x)
{//assert(pphead);//对二级指针进行断言slnode* newnode = buynode(x);if (*pphead == NULL){*pphead = newnode;}else{slnode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}
}
//头插数据
void slpushfront(slnode** pphead, sldatatype x)
{assert(pphead);slnode* newnode = buynode(x);//新的节点的指针原本指向空,现在指向头节点;newnode->next = *pphead;*pphead = newnode;
}
//尾部删除数据
void slpopback(slnode** pphead)
{assert(pphead && *pphead);slnode* pre = *pphead;slnode* ptail = *pphead;if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{while (ptail->next){pre = ptail;ptail = ptail->next;}free(ptail);ptail = NULL;pre->next = NULL;}
}
//头部删除数据
void slpopfront(slnode** pphead)
{assert(*pphead && pphead);slnode* next = (*pphead)->next;free(*pphead);*pphead = next;
}
//查找数据
slnode* slfind(slnode* phead, sldatatype x)
{slnode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;//没有找到
}
//指定的位置之前插入数据
slnode* slinsert(slnode** pphead, slnode* pos, sldatatype x)
{assert(pphead && *pphead);assert(pos);slnode* newnode = buynode(x);if (pos == *pphead)//pos就是链表的第一个节点{slpushfront(pphead, x);}else{slnode* pre = *pphead;while (pre->next != pos){pre = pre->next;}newnode->next = pos;pre->next = newnode;}
}
//在指定的位置之后插入数据
slnode* slinsertafter(slnode* pos, sldatatype x)
{assert(pos);slnode* newnode = buynode(x);newnode->next = pos->next;pos->next = newnode;
}
//删除某个节点
void slerase(slnode** pphead, slnode* pos)
{assert(pphead&&*pphead);assert(pos);slnode* pre = *pphead;if (pos == *pphead){slpopfront(pphead);}else{slnode* pre = *pphead;while (pre->next != pos){pre = pre->next;}pre->next = pos->next;free(pos);pos = NULL;}
}
//删除指定位置后面节点
void sleraseafter(slnode* pos)
{assert(pos && pos->next);slnode* del = pos->next;pos->next = del->next;free(del);del = NULL;
}
//销毁
void destory(slnode** pphead)
{assert(pphead && *pphead);slnode* pcur = *pphead;while (pcur){slnode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
}

(3)test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"slist.h"
void slnodetest01()
{slnode* node1 = (slnode*)malloc(sizeof(slnode));node1->data = 1;slnode* node2 = (slnode*)malloc(sizeof(slnode));node2->data = 2;slnode* node3 = (slnode*)malloc(sizeof(slnode));node3->data = 3;slnode* node4 = (slnode*)malloc(sizeof(slnode));node4->data = 4;node1->next = node2;node2->next = node3;node3->next = node4;node4->next = NULL;slnode* plist = node1;slnodeprint(plist);
}
void test2()
{slnode* plist = NULL;slpushback(&plist, 1);slnodeprint(plist);slpushback(&plist, 2);slpushback(&plist, 9);slpushback(&plist, 4);slnodeprint(plist);slpushfront(&plist, 6);slnodeprint(plist);//6 1 2 9 4;/*slpopback(&plist);slnodeprint(plist);slpopfront(&plist);slnodeprint(plist);*/slnode* find = slfind(plist, 9);/*if (find == NULL){printf("没有找到");}else{printf("找到了");}*///slerase(&plist, find);//slnodeprint(plist);//注意这个里面的55,56不能和58,60同时执行,必须要注释一个sleraseafter(find);//slinsert(&plist, find, 10);slnodeprint(plist);destory(&plist);slnodeprint(plist);}
int main()
{//slnodetest01();test2();return 0;
}

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

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

相关文章

【unity】【C#】UGUI组件

文章目录 UI是什么对UI初步认识 UI是什么 UI是用户界面&#xff08;User Interface&#xff09;的缩写&#xff0c;它是用户与软件或系统进行交互的界面。UI设计旨在提供用户友好的界面&#xff0c;使用户能够轻松地使用软件或系统。UI设计包括界面的布局、颜色、字体、图标等…

Github Benefits 学生认证/学生包 新版申请指南

本教程适用于2024年之后的Github学生认证申请&#xff0c;因为现在的认证流程改变了很多&#xff0c;所以重新进行了总结这方面的指南。 目录 验证教育邮箱修改个人资料制作认证文件图片转换Base64提交验证 验证教育邮箱 进入Email settings&#xff0c;找到Add email address…

Java集合List

List特有方法 经典多态写法 // 经典的多态写法 List<String> list new ArrayList<>();常用API&#xff1a;增删改查 // 添加元素 list.add("Java"); // 添加元素到指定位置 list.add(0, "Python");// 获取元素 String s list.get(0);// 修改…

Docker容器嵌入式开发:在Ubuntu上配置Postman和flatpak

在 Ubuntu 上配置 Postman 可以通过 Snap 命令完成&#xff0c;以下是所有命令的总结&#xff1a; sudo snap install postmansudo snap install flatpak在 Ubuntu 上配置 Postman 和 Flatpak 非常简单。以下是一些简单的步骤&#xff1a; 配置 Flatpak 安装 Flatpak&#x…

【Linux】环境下OpenSSH升级到 OpenSSH_9.6P1(图文教程)

漏洞描述 OpenSSH&#xff08;OpenBSD Secure Shell&#xff09;是加拿大OpenBSD计划组的一套用于安全访问远程计算机的连接工具。该工具是SSH协议的开源实现&#xff0c;支持对所有的传输进行加密&#xff0c;可有效阻止窃听、连接劫持以及其他网络级的攻击。OpenSSH 9.6之前…

Qt5 编译 Qt Creator 源码中的 linguist 模块

文章目录 下载 Qt Creator 源码手动翻译多语言自动翻译多语言 下载 Qt Creator 源码 Github: https://github.com/qt/qttools 笔记打算用 Qt 5.12.12 来编译 qt creator-linguist 所以笔者下载的是 tag - 5.12.12 &#xff0c;解压后如下&#xff0c;先删除多余的文件&#xf…

vue + element plus:ResizeObserver loop completed with undelivered notifications

ResizeObserver loop completed with undelivered notifications. 解释&#xff1a; 这个错误通常表示ResizeObserver无法在一个浏览器帧中传递所有的通知&#xff0c;因为它们需要的处理时间比帧的剩余时间更长。这通常发生在被观察元素的尺寸变化导致了一连串的回调函数被调…

51单片机 DS1302

DS1302 实现流程 将提供的ds1302底层参考程序拷贝到工程下 注意在ds1302.c中可能硬件引脚没有定义&#xff0c;注意去看一下。还有头文件什么的在ds1302中记得加上 参考代码&#xff1a; #include "reg52.h" #include "ds1302.h"unsigned char Write_…

深度解析SPARK的基本概念

关联阅读博客文章&#xff1a; 深入理解MapReduce&#xff1a;从Map到Reduce的工作原理解析 引言&#xff1a; 在当今大数据时代&#xff0c;数据处理和分析成为了企业发展的重要驱动力。Apache Spark作为一个快速、通用的大数据处理引擎&#xff0c;受到了广泛的关注和应用。…

使用QT 开发不规则窗体

使用QT 开发不规则窗体 不规则窗体贴图法的不规则窗体创建UI模板创建一个父类创建业务窗体main函数直接调用user_dialog创建QSS文件 完整的QT工程 不规则窗体 QT中开发不规则窗体有两种方法&#xff1a;&#xff08;1&#xff09;第一种方法&#xff0c;使用QWidget::setMask函…

缓存相关知识总结

一、缓存的作用和分类 缓存可以减少数据库的访问压力&#xff0c;提升整个网站的数据访问速度&#xff0c;改善数据库的写入性能。缓存可以分为两种&#xff1a; 缓存在应用服务器上的本地缓存&#xff1a;访问速度快&#xff0c;但受应用服务器内存限制 缓存在专门的分布式缓存…

【网络安全技术】——网络安全设备(学习笔记)

&#x1f4d6; 前言&#xff1a;网络防火墙&#xff08;简称为“防火墙”&#xff09;是计算机网络安全管理中应用最早和技术发展最快的安全产品之一。随着互联应用的迅猛发展&#xff0c;各种安全问题和安全隐患日渐突出。防火墙及相关安全技术能够最大可能地解决各类安全问题…

官网下载IDE插件并导入IDE

官网下载IDEA插件并导入IDEA 1. 下载插件2. 导入插件 1. 下载插件 地址&#xff1a;https://plugins.jetbrains.com/plugin/21068-codearts-snap/versions 说明&#xff1a;本次演示以IDEA软件为例 操作&#xff1a; 等待下载完成 2. 导入插件 点击File->setting->Pl…

Oracle数据库imp文件导入失败提示:“不是有效的导出文件, 标头验证失败”解决方法

导入数据库时&#xff0c;直接提示不是有效的导出文件&#xff0c;标头验证失败 原因&#xff1a;这是因为导出的imp文件和你当前导入的数据库版本不一致造成的&#xff0c;例如&#xff1a;导出文件版本号12.0.1 导入数据库的版本号11.0.2&#xff0c;会报这个错误。 解决办法…

Node.js模块URL的使用

引入 URL 模块 要使用 URL 模块&#xff0c;首先需要在代码中引入它。可以使用以下代码将 URL 模块导入到你的脚本中&#xff1a; const url require(url);实例代码 const urlrequire(url); var apihttp://www.baidu.com?nameshixiaobin&age20; console.log(url.parse(…

RUM 最佳实践-交互延迟的探索与发现

FID 在互联网高速发展的时代&#xff0c;用户体验已成为企业竞争的关键所在。网页性能作为用户体验的重要组成部分&#xff0c;直接影响着用户的满意度和工作效率。First Input Delay&#xff08;FID&#xff09;作为衡量网页性能的重要指标&#xff0c;越来越受到业界关注。今…

迷宫 — — 蓝桥杯(动态规划)

迷宫 题目&#xff1a; 输入样例&#xff1a; 3 1 1 1 2 3 4 5 6 7 8 9 2 2 1 3 1 R输出样例&#xff1a; 21思路&#xff1a; 题目大意&#xff1a;给定一个n x m的平面网格&#xff0c;并且每一个格子都有一定的代价&#xff0c;并且设有障碍物和陷阱&#xff0c;障碍物的意…

Redux和Redux Toolkit

Redux 概念&#xff1a;redux是react最常用的集中状态管理工具&#xff0c;类似于Vue中的Pinia(vuex)&#xff0c;可以独立于框架运行作用&#xff1a;通过集中管理的方式管理应用的状态 Redux快速体验 不和任何框架绑定&#xff0c;不使用任何构建工具&#xff0c;使用纯Re…

MySQL-创建和管理表:基础知识、创建和管理数据库、创建表、修改表、重命名表、删除表、清空表、拓展

创建和管理表 1. 基础知识1.1 一条数据存储的过程1.2 标识符命名规则1.3 MySQL中的数据类型 2. 创建和管理数据库2.1 创建数据库2.2 使用数据库2.3 修改数据库2.4 删除数据库 3. 创建表3.1 创建方式13.2 创建方式23.3 查看数据表结构 4. 修改表4.1 追加一个列4.2 修改一个列4.3…

mac电脑安装软件报错:无法检查更新,请检查你的互联网连接

1、点菜单栏搜索图标&#xff0c;输入&#xff1a;终端 &#xff0c;找到后&#xff0c;点击打开 2、输入以下命令&#xff1a;&#xff08;复制粘贴进去&#xff09;回车安装 /usr/sbin/softwareupdate --install-rosetta --agree-to-license 3、提示【Install of Rosetta …