--链表--

news2024/9/30 11:38:15

一.链表的概述

二.逻辑图

三.代码详解

//1.定义关于链表的结构体
#include <iostream>
#include <stdlib.h>
#include <assert.h>
using namespace std;
typedef int SLTDateType;//适用于不同的数据类型
typedef struct SListNode
{
	SLTDateType data;//数据
	struct SListNode* next;//结构体指针,存的是下一个节点的地址
}SLTNode;//该结构体的别名
//2.打印链表,用于测试接口
void SListPrint(SLTNode* phead)//链表中的第一个节点
{
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->",cur->data);
		cur = cur->next;//找到下一个节点的地址
	}
	cout << endl;
}
//3.创建节点,便于数据的插入
SLTNode* BuyListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//新增一个节点
	assert(newnode != NULL);
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
//4.在尾部插入一个节点
void SListPushBack(SLTNode** pphead,SLTDateType x)//链表本身就是由一个一个指针组成的,要改变指针的内容,只能用指针的指针
{
	//原来的尾部指向新的节点,新的节点指向空
	//插入一个节点,需将链表扩容,增加一个节点
	/*SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;
	newnode->next = NULL;*/
	SLTNode* newnode = BuyListNode(x);
	//链表本身为空
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	//链表不为空
	else
	{
		//找到尾节点
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

改变:

1)int--->int*

2)int*--->int**(二级指针)

//5.在头部插入一个节点
void SListPushFront(SLTNode** pphead,SLTDateType x)
{
	SLTNode* newnode = BuyListNode(x);
	//先把头节点的地址传给新节点,然后头节点就是空的,即新节点
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}
//6.尾删
//当删除多个节点时,要将数组置为NULL
void SListPopBack(SListNode** pphead)
{
	//法1:需要定义两个变量,若只定义一个变量会导致free后,出现野指针问题(tail在free时,已经将空间还给系统,tail=NULL无效;tail前一个空间的指针指向它,属于野指针)
	SLTNode* tail = *pphead;
	SLTNode* prev = NULL;
	assert(*pphead != NULL);//链表为空,则不能进行尾删
	if ((*pphead)->next == NULL)
	{
		//当链表中只有一个节点时
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		//直到tail->为空,才结束循环,即tail已经为链表中的最后一个节点
		free(tail);
		prev->next = NULL;
		//法2:少定义一个变量(逻辑同上)
		//while (tail->next->next)
		//{
		//	tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;
	}
}
//7.头删
void SListPopFront(SListNode** pphead)
{
	//不能直接free掉头节点,头节点会被置成随机值,导致找不到下一个节点
	//与尾删对比,头删不需要再定义一个新的变量,来指向前面的一个节点
	assert(*pphead != NULL);//当链表不为空时
	SListNode* next = (*pphead)->next;//定义一个指针来指向头节点的下一个地址
	free(*pphead);
	*pphead = next;//完成使头节点指向下一个节点的地址
}
//8.查找
SLTNode* SListFind(SLTNode* phead,SLTDateType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;//查询不到,返回空
}
//9.在pos位置前插入一个节点
void SListInsert(SLTNode** pphead,SLTNode* pos,SLTDateType x)//使用二级指针对链表进行修改
{
	//单链表不适合在pos位置前插入数据,会有一定程度的效率损失,在pos后面插入,不需要传入plist
	//可配合find函数进行插入
	//创建一个新的节点
	SLTNode* newnode = BuyListNode(x);
	if (*pphead==pos)//否则在头部插入节点时,会报错,因为找不到该位置的前一个节点
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
	else
	{
		//找到pos的前一个位置
		SLTNode* posPrev = *pphead;
		while (posPrev->next != pos)
		{
			posPrev = posPrev->next;
		}
		posPrev->next = newnode;
		newnode->next = pos;
	}
}
//10.在pos位置后插入一个节点
void SListInsertAfter(SLTNode* pos,SLTDateType x)
{
	//要先改newnode后的那个节点,先改pos节点的话,会导致newnode找不到下一个节点的地址
	//先创建出一个新的节点
	SLTNode* newnode = BuyListNode(x);
	newnode->next=pos->next;
	pos->next = newnode;
}
//11.删除pos位置前的一个节点
void SListErase(SLTNode** pphead, SLTNode* pos)
{
	if (*pphead == pos)//相当于头删
	{
		*pphead=pos->next;
		free(pos);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next!=pos)
		{
			prev = prev->next;
		}
		prev->next=pos->next;
		free(pos);
		/*pos = NULL;*///无意义,可直接删除
	}
}
//12.删除pos位置后的一个节点
void SListEraseAfter(SLTNode** pphead,SLTNode* pos)
{
	//与删除pos前一个位置的节点对比,删除后一个位置时,不用找pos的前一个节点
	SLTNode* next = pos->next;//记录要删除的节点
	pos->next=next->next;
	free(next);
}
//13.销毁链表
void SListDestory(SLTNode** pphead)
{
	//不能free掉头节点,会导致找不到下一个节点的地址
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;//先设置一个变量用来保存cur所指的下一个节点
		free(cur);
		cur = next;
	}
	*pphead = NULL;//链表的头部要滞空,最后一个不用滞空,在函数里面会自动滞空
}

每写一个接口函数,就要进行一次测试,以下为所有测试函数:

void SListTest()//测试函数
{
	SLTNode* plist = NULL;//初始化链表
	cout << "尾部插入节点测试:" << endl;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushBack(&plist, 5);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "头部插入节点测试:" << endl;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPushFront(&plist, 5);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "尾删测试:" << endl;
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "头删测试:" << endl;
	SListPopFront(&plist);
	SListPrint(plist);
	cout << "---------------" << endl;
	//查找测试
	//当链表中的多个2时(利用头插法实现模拟)
	SListPushFront(&plist,2);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 2);
	cout << "新的链表为:" << endl;
	SListPrint(plist);
	cout << "查找测试:" << endl;
	SLTNode* pos = SListFind(plist, 2);
	int i = 1;
	while (pos)//当pos没有移动到最后一个节点时,继续查找
	{
		cout << "第" << i << "个节点" << ":" << pos->data << endl;
		i++;
		pos = SListFind(pos->next, 2);
	}
	//可利用查找函数对链表中的数值进行修改
	cout << "---------------" << endl;
	cout << "修改数据测试:" << endl;
	pos = SListFind(plist,3);
	if (pos)
	{
		pos->data = 29;
	}
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "删除pos位置后的节点测试:" << endl;
	SListEraseAfter(&plist,pos);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "在pos位置前插入节点测试:" << endl;
	SListInsert(&plist, pos, 34);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "在pos位置后插入节点测试:" << endl;
	SListInsertAfter(pos,7);
	SListPrint(plist);
	cout << "---------------" << endl;
	cout << "删除pos位置前的节点测试:" << endl;
	SListErase(&plist,pos);
	SListPrint(plist);
	//cout << "---------------" << endl;
	//cout << "销毁链表测试:" << endl;
	//SListDestory(&plist);
	//SListPrint(plist);
}
int main()
{
	SListTest();
	return 0;
}

运行结果如图:

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

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

相关文章

【Day14-单例设计模式动态代理】

单例设计模式 什么是设计模式&#xff08;Design pattern&#xff09; ? 一个问题通常有n种解法&#xff0c;其中肯定有一种解法是最优的&#xff0c;这个最优的解法被人总结出来了&#xff0c;称之为设计模式。设计模式有20多种&#xff0c;对应20多种软件开发中会遇到的问题…

记录开发一个英语听力训练网站

背景 在当前全球经济衰退的背景下&#xff0c;IT相关的工作在国内的竞争也是越来越激烈&#xff0c;为了能够获得更多的可能性&#xff0c;英语的学习也许能为程序员打开一扇新的窗户&#xff0c;比如很多远程的工作尤其是国际化背景的工作团队&#xff0c;英语的协作沟通是必…

Modbus通信

Modbus是一种经典的工业通信协议&#xff0c;由Modicon&#xff08;现为施耐德电气&#xff09;在1979年首次发布。它广泛应用于各种工业自动化系统中&#xff0c;尤其是在PLC&#xff08;可编程逻辑控制器&#xff09;与其他设备之间的通信。Modbus的主要特点是其简单性和开放…

《Oracle(一)- 基础》

文章目录 一、Oracle简介&#xff08;一&#xff09;什么是ORACLE&#xff08;二&#xff09;ORACLE 体系结构1.数据库2.实例3.数据文件&#xff08;dbf&#xff09;4.表空间5.用户 二、ORACLE 安装与配置&#xff08;一&#xff09;VMware 挂载 windows server 2003&#xff0…

海外短剧系统一站式开发+h5,app双端

前言&#xff1a; 海外短剧是指那些制作时间短、剧情紧凑、内容丰富的剧集&#xff0c;主要在海外市场&#xff08;如北美、欧洲、东南亚等&#xff09;播放并受到欢迎。 而海外短剧系统是指一种用于制作和播放海外短剧的系统。该系统通常由电视台、制片公司或在线视频平台使…

C++:STL详解(一)string类的基本介绍与使用方式

✨ Blog’s 主页: 白乐天_ξ( ✿&#xff1e;◡❛) &#x1f308; 个人Motto&#xff1a;实践是检验真理的唯一标准&#xff01;&#xff01;&#xff01;敲代码需要勤快点&#xff01;&#xff01;&#xff01;&#xff01; &#x1f4ab; 欢迎来到我的学习笔记&#xff0…

十二,Spring Boot 异常处理(自定义异常页面,全局异常,自定义异常)

十二&#xff0c;Spring Boot 异常处理(自定义异常页面&#xff0c;全局异常&#xff0c;自定义异常) 文章目录 十二&#xff0c;Spring Boot 异常处理(自定义异常页面&#xff0c;全局异常&#xff0c;自定义异常)1. 基本介绍2. 自定义异常页面3. 全局异常4. 自定义异常5. 补充…

LineageOS刷机教程

版权归作者所有&#xff0c;如有转发&#xff0c;请注明文章出处&#xff1a;https://cyrus-studio.github.io/blog/ LineageOS 是一个基于 Android 开源项目&#xff08;AOSP&#xff09;的开源操作系统&#xff0c;主要由社区开发者维护。它起源于 CyanogenMod 项目&#xff…

数据库索引底层数据结构之B+树MySQL中的页索引分类【纯理论知识,干货分享,面试必备】

目录 1、索引简介 1.1 什么是索引 1.2 使用索引的原因 2、索引中数据结构的设计 —— B树 2.1 哈希 2.2 二叉搜索树 2.3 B树 2.4 最终选择之——B树 2.4.1 B树与B树的对比(面向索引)【面试题】 3、MySQL中的页 3.1 页的使用原因 3.2 页的结构 3.2.1 页文件头和页文件…

解锁定位服务:Flutter应用中的高德地图定位

前言 在现代移动应用开发中&#xff0c;定位服务已成为一项基本功能&#xff0c;它使得应用能够获取用户的地理位置信息&#xff0c;为用户提供更加个性化的服务。 Flutter 作为跨平台的移动应用开发框架&#xff0c;支持集成多种服务&#xff0c;包括定位服务。 本文将介绍如…

HR8870:可PWM控制,4.5A直流有刷电机驱动数据手册

HR8870芯片描述 HR8870是一款直流有刷电机驱动器&#xff0c;适用于打印机、电器、工业设备以及其他小型机器。两个逻辑输入控制H桥驱动器&#xff0c;该驱动器由四个N-MOS组成&#xff0c;能够以高达4.5A的峰值电流双向控制电机。利用电流衰减模式&#xff0c;可通过对输入进行…

故障码格式解析

中&#xff0c;诊断故障码&#xff08;DTC, Diagnostic Trouble Code&#xff09;是由一个字母前缀和三个后续字符组成的。这些字母前缀根据故障所属的系统类别来区分&#xff0c;具体如下&#xff1a; B0 -- B3&#xff1a;表示车身系统&#xff08;Body&#xff09;的故障码…

Linux CTF逆向入门

1.ELF格式 我们先来看看 ELF 文件头&#xff0c;如果想详细了解&#xff0c;可以查看ELF的man page文档。 关于ELF更详细的说明&#xff1a; e_shoff&#xff1a;节头表的文件偏移量&#xff08;字节&#xff09;。如果文件没有节头表&#xff0c;则此成员值为零。 sh_offset&…

Qt 菜单、工具栏 的基本使用

效果 代码 #include "mainwindow.h" #include "ui_mainwindow.h" #include<QToolBar> #include<QDebug> #include<QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupU…

【JAVA入门】Day45 - 压缩流 / 解压缩流

【JAVA入门】Day45 - 压缩流 / 解压缩流 文章目录 【JAVA入门】Day45 - 压缩流 / 解压缩流一、解压缩流二、压缩流 在文件传输过程中&#xff0c;文件体积比较大&#xff0c;传输较慢&#xff0c;因此我们发明了一种方法&#xff0c;把文件里的数据压缩到一种压缩文件中&#x…

【LLMs对抗性提示:提示泄漏、非法行为、DAN、Waluigi效应、 游戏模拟器、防御策略————】

对抗性提示 目录 对抗性提示 提示注入 提示泄漏 非法行为 DAN Waluigi效应 GPT-4模拟器 游戏模拟器 防御策略 在指令中添加防御 参数化提示组件 引用和其他格式 对抗提示检测器 模型类型 参考文献 Adversarial prompting是提示工程中的一个重要主题&#xff0c…

每日OJ_牛客_NC313 两个数组的交集

目录 牛客_NC313 两个数组的交集 解析代码 牛客_NC313 两个数组的交集 两个数组的交集_牛客题霸_牛客网 class Solution { public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** * param nums1 int整型vector * pa…

统计/nginx/access.log中每个ip的访问次数,按高到低排列

/nginx/access.log具体内容长这样&#xff1a; 第一个元素就是ip。 awk {print $1} /nginx/access.log | sort | uniq -c | sort -r首先&#xff0c;awk {print $1} /nginx/access.log 从 /nginx/access.log文件的每行中提取出第一个字段。然后&#xff0c;sort 对提取出的第…

【有哪些坑】Apollo配置中心FAQ常见问题列表

使用某个框架之前&#xff0c;得先看看前辈们踩过的坑。 他人的间接经验 -> 自己的直接经验 前车之鉴&#xff0c;后事之师。比喻前人失败了&#xff0c;后人应该从中吸取教训&#xff0c;避免再犯同样的错误。 常见问题回答 1. Apollo是什么&#xff1f; Apollo&#xff…

关于STM32项目面试题01:电源

博客的风格是&#xff1a;答案一定不能在问题的后面&#xff0c;要自己想、自己背&#xff1b;回答都是最精简、最精简、最精简&#xff0c;可能就几个字&#xff0c;你要自己自信的展开。 面试官01&#xff1a;说说你知道的开关电源的拓扑结构&#xff1f; 面试官02&#xff1…