初阶数据结构之单链表的实现(四)

news2024/12/26 13:41:49

文章目录

  • 链表的概念及结构
  • 一、链表的逻辑结构是什么样的?
  • 二、链表的初始化
    • 2.1链表初始化的示意
    • 2.2链表初始化代码实现
  • 三、链表的各类接口函数定义
  • 四、链表的各类接口函数的代码实现
    • 4.1链表的打印代码实现
      • 4.1.1打印代码的实现
    • 4.2链表的尾插代码实现
      • 4.2.1尾插代码实现
    • 4.3链表的头插代码实现
      • 4.3.1头插代码的实现
    • 4.4BuyLTNode函数的实现
    • 4.5链表的尾删代码实现
      • 4.5.1尾删代码的实现
    • 4.6链表的头删代码实现
      • 4.6.1头插代码的实现
  • 五、完整代码的实现
  • 总结


链表的概念及结构

链表的概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。


一、链表的逻辑结构是什么样的?

所谓的逻辑结构其实就是能让我们自己能够更好的理解这个东西是什么?那么我们就用画图来理解理解吧!!在单链表中,存放着两个量,一个是地址量,一个是数据量,如图所示,这是一个带头的单链表。

在这里插入图片描述

二、链表的初始化

2.1链表初始化的示意

正如上图所说,单链表中存在一个数据量和一个地址量,所以我们定义结构体的时候,应该定一个data数据量,和一个指针用来存放自己所以定义的地址量。

2.2链表初始化代码实现

代码如下:

typedef int SLTDataType;

typedef struct SListnode
{
	SLTDataType data;
	struct SListnode* next;
}SLTNode;

并且还需要定义几个头文件如下所示

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

这里介绍一个查询头文件的网站CPlusplus


三、链表的各类接口函数定义

void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);
void SLEraseAfter(SLTNode* pos);

void SLTDestroy(SLTNode* phead);

四、链表的各类接口函数的代码实现

4.1链表的打印代码实现

因为我们写的是带头的单链表,所以我们就可以从头开始遍历。我们定义一个结构体指针用来存放头结点,因为头结点此时存放的是下一个位置的地址,所以我们只需要拿一个指针去接收它就行啦。

那我们此时该思考一下,我们虽然是通过遍历来打印数据,但是我们是不是得想一下结束条件呢?如果我们用cur->next == NULL 作为结束条件,那么我们是不是想错了呢?我们画图解释一下吧!从图中看,如果我们以cur->next为结束条件那么,最后一个数我们是不是就打印不了,所以我们就想错啦

在这里插入图片描述

我们在来看看下图,我们发现用cur本身去遍历就可以把全部都遍历完,好啦这里我们算是解释通啦!

在这里插入图片描述

4.1.1打印代码的实现

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

4.2链表的尾插代码实现

尾插顾名思义就是在尾部进行插入,那么我们应该怎么样去实现单链表的尾部插入呢?我们还是画图进行理解,如下图所示。

我们想要在尾部进行插入,我们是不是应该先遍历到尾结点呢?是的没错!但是这里我们就得注意我们遍历结束的条件了,为什么这么说呢?假设如果按照我们上面打印那样遍历的话,我们就会遍历成野指针,所以我们遍历的条件为cur->next ,这样是不是就解决啦!

在这里插入图片描述

这里我们得考虑两种情况。
1、当这个链表是空的时候我们应该怎么去处理他呢?我们直接让新建立的节点赋值给我们的头就行啦
2、也就是正常的插入啦!让最后一个节点的指针指向新节点,然后再把最后一个节点的位置改变就完成啦!

4.2.1尾插代码实现

void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	if ((*pphead) == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

4.3链表的头插代码实现

关于头插我们应该这样处理:
1、因为我们是带有头结点的单链表,又因为我们头结点存储的是第一个节点的地址,所以只需要把头结点所存储的地址赋给新开的节点的next位置,此时的新节点所指向的就是原来第一个节点的位置
2、因为头结点存储第一个节点的地址,所以我们要把新开的节点的地址赋给头结点,这样就完成了头插!

1图文解释:插入前:

在这里插入图片描述

2、插入中:

在这里插入图片描述

3、结束插入:
在这里插入图片描述

4.3.1头插代码的实现

void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

4.4BuyLTNode函数的实现

本函数的意思是动态的开辟一个节点在堆上!

SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

4.5链表的尾删代码实现

在单链表中,尾删的效率其实是非常的差的,因为单链表需要一直遍历查找,所以效率耗费的十分的严重。

在尾删的过程中,我们也需要考虑两种情况!
第一种情况:当链表中只有一个节点的时候我们应该怎么样的删除
第二种情况:当链表中有多个节点的时候我们应该怎么删除呢?

当只有一个节点的时候,我们应该直接把节点free掉,因为我们的节点都是动态开辟的,所以我们应用free这个函数,把节点摧毁掉

当有多个节点的时候,我们应该用两个指针,一个在前面一个在后面,当前面的节点为空时,说明我们找到了尾,我们free掉尾结点就行啦!

4.5.1尾删代码的实现

void SLTPopBack(SLTNode** pphead)
{
	//当只有一个节点的时候
	//多个节点
	assert(pphead);
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

4.6链表的头删代码实现

如下图所示,先让pphead往前先走一步,提前找好一个前节点,然后free掉他,然后把pphead赋值给prev,再让*pphead往前走!

在这里插入图片描述
在这里插入图片描述

4.6.1头插代码的实现

void SLTPopFront(SLTNode** pphead)
{
	//空链表
	assert(*pphead);
	assert(pphead);
	SLTNode* prev = *pphead;
	*pphead = prev->next;
	free(prev);
}

五、完整代码的实现

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"listnode.h"

void test1()
{
	SLTNode* plist = NULL;
	SLTPushFront(&plist, 0);
	SLTPushFront(&plist, 1);
	SLTPushFront(&plist, 2);
	SLTPushFront(&plist, 3);
	SLTPrint(plist);
	SLTPushBack(&plist, -1);
	SLTPrint(plist);
}
void test2()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, -1);
	SLTPushBack(&plist, 0);
	SLTPushBack(&plist, 1);
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPopBack(&plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
}
void test3()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, -1);
	SLTPushBack(&plist, 0);
	SLTPushBack(&plist, 1);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPopFront(&plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}
void test4()
{
	SLTNode* plist = NULL;
	SLTPushBack(&plist, 1);
	SLTPushBack(&plist, 2);
	SLTPushBack(&plist, 3);
	SLTPushBack(&plist, 4);
	SLTPrint(plist);
	SLTNode* pos = SLTFind(plist, 3);
	pos->data = 30;
	SLInsert(&plist, pos, 20);
	SLTPrint(plist);
	SLInsertAfter(pos, 40);
	SLTPrint(plist);
	SLEraseAfter(pos);
	SLTPrint(plist);
	SLTDestroy(plist);
}
int main()
{
	test4();
	return 0;
}

listnode.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "listnode.h"

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

SLTNode* BuyLTNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	if ((*pphead) == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
//尾删的第一种方式
void SLTPopBack(SLTNode** pphead)
{
	//当只有一个节点的时候
	//多个节点
	assert(pphead);
	assert(*pphead);
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		SLTNode* prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}
//尾删的第二种方式
/*
void SLTPopBack(SLTNode** pphead)
{
	SLTNode* tail = *pphead;
	while (tail->next->next)
	{
		tail = tail->next;
	}
	free(tail->next);
	tail->next = NULL;
}
*/
void SLTPopFront(SLTNode** pphead)
{
	//空链表
	assert(*pphead);
	assert(pphead);
	SLTNode* prev = *pphead;
	*pphead = prev->next;
	free(prev);
	//一个节点
	//if ((*pphead)->next == NULL)
	//{
	//	free(*pphead);
	//	*pphead = NULL;
	//}
	多个节点
	//else
	//{
	//	SLTNode* prev = *pphead;
	//	*pphead = prev->next;
	//	free(prev);
	//}
}

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


//在pos之前插入
void SLInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuyLTNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
	
}
void SLInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = BuyLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = pos->next;
		free(pos);
	}
}
void SLEraseAfter(SLTNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLTNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

void SLTDestroy(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur)
	{
		phead = phead->next;
		free(cur);
		cur = phead;
	}
}

listnode.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLTDataType;

typedef struct SListnode
{
	SLTDataType data;
	struct SListnode* next;
}SLTNode;
 
void SLTPrint(SLTNode* phead);
void SLTPushBack(SLTNode** pphead, SLTDataType x);
void SLTPushFront(SLTNode** pphead, SLTDataType x);

void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SLInsert(SLTNode** pphead,SLTNode* pos, SLTDataType x);
void SLInsertAfter(SLTNode* pos, SLTDataType x);
//删除pos位置的值
void SLErase(SLTNode** pphead, SLTNode* pos);
void SLEraseAfter(SLTNode* pos);

void SLTDestroy(SLTNode* phead);

总结

好啦,今天的单链表的大部分解决思路我都给大家详细的写出来啦!我们下期再见!嘿我们天天见!!!

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

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

相关文章

Mongodb 6.0 变化的配置参数与连接的方式变化

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

【故障排查】【伪处理】Jenkins在iframe窗口无法添加secret text凭证

问题描述 环境的ingress是&#xff0c;有个 svc 暴露 30080 -> 80 大致流程&#xff1a; svc&#xff08;ingress-nginx&#xff09; 30080端口 -> pod (nginx-ingress-controller ) 80端口 -> ingress -> svc -> pod &#xff08;Jenkins 8080端口&#xff0…

基于粒子群优化算法的路径问题优化研究及其在Python中的实现

基于粒子群优化算法的路径问题优化研究及其在Python中的实现 文章目录 基于粒子群优化算法的路径问题优化研究及其在Python中的实现一、简介二、引言1、粒子群优化算法简介2、PSO算法与鸟群的类比 三、PSO算法数学原理1、粒子的位置和速度2、适应度函数3、粒子的个体最优和全局…

基于html+css的图展示80

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

从零开始搭建属于自己的hexo博客

前提要素&#xff1a;一个提供博客运行的服务器或者XX云。 先展示一下搭建好的样子&#xff0c;给大家一点动力&#xff1a; 整个搭建过程大概需要30分钟不到&#xff0c;请各位控制自己的时间~ 好了&#xff0c;废话不多说我们开始&#xff01; 1、首先登录我们准备好的服…

redis高级篇二(分片集群)

一)进行测试Sentinel池: ​ Controller public class RestController {RequestMapping("/Java100")ResponseBodypublic String start(){//1.配置信息HashSet<String> setnew HashSet<>();// 连接信息 ip:port// set.add("127.0.0.1:27001");se…

[抓包] 微信小程序(PC版)如何抓包

本文抓包环境 Win10&#xff0c;微信(PC版本) v3.9.2.20&#xff0c;Burp Suite v1.7.32&#xff0c;Proxifier v3.42 不使用安卓模拟器 一、下载安装好Burp Suite&#xff0c;Proxifier 链接: https://pan.baidu.com/s/177BIEgCmZG9MfqQ4D0PTsg 提取码: 3hex 二、开始抓包 …

【ASP.NET Core笔记】 使用razor pages构建网站

使用ASP.NET Core Razor Pages 构建网站 sqlite 北风数据库 1. Northwind.Common.DataContext.Sqlite 是Sqlite的数据库上下文&#xff0c;有三个类&#xff1a;ConsoleLogger.csNorthwindContext.csNorthwindContextExtensions.cs 1.1 NorthwindContext 继承自 Microsoft.Enti…

计算机图形学-GAMES101-10

一、纹理 原模型网格->逐面Shading->逐像素Phong Shading->纹理贴图->换个纹理再贴图->环境光照。 &#xff08;1&#xff09;环境贴图 纹理就是一张图&#xff0c;纹理是GPU里的一块内存&#xff0c;可以进行点查询、范围查询、滤波。  环境光贴图、环境光映…

The authenticity of host ‘gitee.com (212.64.63.190)‘ can‘t be established.

在将本地仓库的代码推送到远程仓库时&#xff0c;出现下面报错。 $ git push -u origin master The authenticity of host gitee.com (212.64.63.190) cant be established. ECDSA key fingerprint is SHA256:FQGC9Kn/eye1W8icdBgrQpKkGYoFgbVr17bmjey0Wc. Are you sure you w…

Kali-linux分析密码

在实现密码破解之前&#xff0c;介绍一下如何分析密码。分析密码的目的是&#xff0c;通过从目标系统、组织中收集信息来获得一个较小的密码字典。本节将介绍使用Ettercap工具或MSFCONSOLE来分析密码。 8.2.1 Ettercap工具 Ettercap是Linux下一个强大的欺骗工具&#xff0c;也…

Mysql中常用到的查询关键字

文章目录 1、join2、like 模糊查询3、or4、distinct5、in 包含6、group by 分组7、order by8、limit 1、join MySQL 的连接主要分为内连接和外连接。 什么是内连接&#xff1a; 取得两张表中满足存在连接匹配关系的记录。 什么是外连接&#xff1a; 不只取得两张表中满足存在…

设计高端大气上档次的时尚品?!微软工程师借助AIGC成功圆梦!

&#xff08;本文阅读时间&#xff1a;5 分钟&#xff09; 在这个创意横飞、想象力爆棚的时代中&#xff0c;有很多美丽且吸引人的东西围绕在我们身边。然而&#xff0c;遗憾的是&#xff0c;被誉为“攻城狮”的我&#xff0c;内心里虽然非常喜欢且欣赏这些夺目的作品&#xff…

手动输入域账号自动禁用windows域用户的脚本

禁用windows域用户的脚本&#xff0c;需求&#xff1a; 1、显示该域用户的创建时间&#xff0c;最后登录时间和存放的OU&#xff0c; 2、将该账号禁用&#xff1b; 3、禁用后重新返回交互界面等待输入新的域账号。 Powershell脚本内如下&#xff1a; # 无限循环等待用户的输入…

Meta没做成的事OpenAI要干?ChatGPT之父推出加密项目Worldcoin

大模型焦虑还未停歇&#xff0c;鬼才创始人瞄准了加密领域。 继ChatPT之后&#xff0c;OpenAI的首席执行官Sam Altman推出的加密货币项目Worldcoin近日被透露融资近一亿美金&#xff0c;彻底搅翻了科技界与Web3的平静。 据官网描述&#xff0c;Worldcoin由Sam和Max Novendstern…

奇舞周刊第492期:提升 Web 核心性能指标的 9 个建议

记得点击文章末尾的“ 阅读原文 ”查看哟~ 下面先一起看下本期周刊 摘要 吧~ 奇舞推荐 ■ ■ ■ 提升 Web 核心性能指标的 9 个建议 大家好&#xff0c;我是 ConardLi。今天继续来为大家解读今年的 Google I/O&#xff0c;我会重点为大家解读前端开发者应该关注的信息&#xff…

软考初级程序员上午单选题(13)

1、下列不能兼作输入设备和输出设备的是______。 A&#xff0e;可擦除型光盘 B&#xff0e;软盘 C&#xff0e;硬盘 D&#xff0e;键盘 2、文件型计算机病毒主要感染______。 A&#xff0e;.TXT文件 B&#xff0e;.GIF文件 C&#xff0e;.EXE文件 D&#xff0e;.MP3文件 3、_…

【Error】Error: Cannot find module ‘babel-preset-es2015‘

启动项目时出现了下面的错误&#xff0c;错误图如下&#xff1a; 解决办法&#xff1a; 在项目的babel.config.js文件中修改presets的参数&#xff0c;修改如下&#xff1a; 原文件&#xff1a; 修改成babel/preset-env&#xff0c;修改后文件&#xff1a;

SpringBoot源码分析之Tomcat是如何在SpringBoot中启动的?

一.前言 我们知道SpringBoot可以直接把传统的war包打成可执行的jar包&#xff0c;直接启动。这得益于SpringBoot内置了容器可以直接启动。本文将以 Tomcat 为例&#xff0c;来看看 SpringBoot 是如何启动 Tomcat 的。 二.源码分析 一.SpringApplication初始化 public class…

【STC8】热启动串口指令下载

前言 在目标开发板没有装载自动下载电路的时候&#xff0c;往往需要冷启动&#xff0c;也就是需要手动开关电源&#xff0c;来达到单片机复位下载。当然还有一种方法是热启动&#xff0c;通过串口接收到自定义的指令后&#xff0c;软件执行复位下载。这就是本文介绍的内容。 材…