线性结构之单链表详解

news2024/11/23 8:44:57

文章目录

  • 前言
  • 一.单链表的结构体
  • 二、单链表的基本接口
    • 1.SListMalloc(申请节点)
    • 2.SListPushBack(尾插)
    • 3.SListPushFront(头插)
    • 4.SListPopBack(尾删)
    • 5.SListPopFront(头删)
    • 6.SListInsert(插入)
    • 7.SListErase(删除)
    • 8.SListFind(查找)
    • 9.SListPrint(打印)
    • 10.SListDestroy(销毁)
  • 三、整体结构
    • 1.头文件
    • 2.源文件
  • 结语


在这里插入图片描述

前言

我们前面学习了顺序表的实现,那么今天我们来学习单链表,当然单链表也有许多种类,在这里我当然是讲不完的,在这里我只介绍单链表最常见的无头单向非循环链表


一.单链表的结构体

typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;//指向下一个节点的指针
}SLN;

在这里插入图片描述

二、单链表的基本接口

1.SListMalloc(申请节点)

SLN* SListMalloc(SLTDataType x)
{
	SLN* newnode = (SLN*)malloc(sizeof(SLN));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

我这里的命名可能不规范,你按照自己的命名习惯来

2.SListPushBack(尾插)

void SListPushBack(SLN** pphead ,SLTDataType x)
{
	SLN*newnode= SListMalloc(x);
	if (*pphead== NULL)//头结点如果为空
	{
		*pphead = newnode;
	}
	else
	{
		SLN* tail = *pphead;
		while (tail->next != NULL)//找到含有NULL节点的位置;即最后的位置;
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

这个函数为什么传参的时候需要二级指针呢?头节点如果为空,我们是需要修改头结点的内容的,头节点是一个一级指针,那么我们修改他就需要二级指针

3.SListPushFront(头插)

void SListPushFront(SLN** pphead, SLTDataType x)
{
	SLN* newnode = SListMalloc(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

在这里插入图片描述

真理不管头指针是不是空指针,我们都是需要修改头指针的,当头插时,我们将新的节点的next指向phead之后,我们就要修改头指针为新的节点,所以我们都需要修改头指针

4.SListPopBack(尾删)

void SListPushBack(SLN** pphead ,SLTDataType x)
{
	SLN*newnode= SListMalloc(x);
	if (*pphead== NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLN* tail = *pphead;
		while (tail->next != NULL)//找到含有NULL节点的位置;即最后的位置;
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

5.SListPopFront(头删)

void SListPushFront(SLN** pphead, SLTDataType x)
{
	SLN* newnode = SListMalloc(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}

既然需要改变头部指针指向,那么就需要传二级指针

6.SListInsert(插入)

void SListInsert(SLN** pphead, SLN* pos, SLTDataType x)//在pos的前面插入
{
	if (pos = *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLN* newnode = SListMalloc(x);
		assert(newnode != NULL);
		SLN* prev = *pphead;
		while (prev->next != pos)//这里需要找到pos的前面的位置,遍历
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}

7.SListErase(删除)

void SListErase(SLN** pphead, SLN* pos)
{
	if (*pphead == pos)
	{
		SListPopBack(pphead);
	}
	else
	{
		SLN* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
	}
}

8.SListFind(查找)

SLN* SListFind(SLN** pphead, SLTDataType x)
{
	SLN* temp = *pphead;
	while (temp)
	{

		if(temp->data == x)
		{
			return temp;
		}
		else
		{
			temp = temp->next;
		}
		
	}
	return NULL;//如果没有找到就返回一个NULL
}

9.SListPrint(打印)

void SListPrint(SLN*phead)
{
	//遍历链表
	SLN* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
}

这里是我个人加的接口,遍历一遍链表

10.SListDestroy(销毁)

void SListDestroy(SLN** pphead)
{
	assert(*pphead != NULL);
	while(*pphead!=NULL)
	{
		SLN* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;
	}
}

这里销毁指的是整个链表的删除

三、整体结构

1.头文件

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

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SLN;
SLN* SListMalloc(SLTDataType x);
void SListPrint(SLN* phead);
void SListPushBack(SLN** phead, SLTDataType x);
void SListPushFront(SLN** pphead, SLTDataType x);
void SListPopBack(SLN** pphead);
void SListPopFront(SLN** pphead);
SLN* SListFind(SLN** pphead,SLTDataType x);
void SListInsert(SLN** pphead, SLN* pos, SLTDataType x);
void SListErase(SLN** pphead, SLN* pos);
void SListDestroy(SLN** pphead);

2.源文件

#include"SList.h"
#include<assert.h>
SLN* SListMalloc(SLTDataType x)
{
	SLN* newnode = (SLN*)malloc(sizeof(SLN));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}
void SListPrint(SLN*phead)
{
	//遍历链表
	SLN* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
}
void SListPushBack(SLN** pphead ,SLTDataType x)
{
	SLN*newnode= SListMalloc(x);
	if (*pphead== NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLN* tail = *pphead;
		while (tail->next != NULL)//找到含有NULL节点的位置;即最后的位置;
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
void SListPushFront(SLN** pphead, SLTDataType x)
{
	SLN* newnode = SListMalloc(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		newnode->next = *pphead;
		*pphead = newnode;
	}
}
void SListPopBack(SLN** pphead)//这里需要考虑NULL,只有一个和两个及两个以上
{
	SLN* tail=*pphead;
	SLN* prev = NULL;
	assert(*pphead != NULL);
	if ((*pphead)->next == NULL)
	{
		*pphead = NULL;
	}
	else
	{
		while (tail->next !=NULL)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}
void SListPopFront(SLN** pphead)//这里不需要用考虑只有一个节点的情况,因为没有引用next
{
	SLN* temp = *pphead;
	assert(*pphead != NULL);
	*pphead = (*pphead)->next;	
	free(temp);
}
SLN* SListFind(SLN** pphead, SLTDataType x)
{
	SLN* temp = *pphead;
	while (temp)
	{

		if(temp->data == x)
		{
			return temp;
		}
		else
		{
			temp = temp->next;
		}
		
	}
	return NULL;
}
void SListInsert(SLN** pphead, SLN* pos, SLTDataType x)//在pos的前面插入
{
	if (pos = *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLN* newnode = SListMalloc(x);
		assert(newnode != NULL);
		SLN* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}
void SListErase(SLN** pphead, SLN* pos)
{
	if (*pphead == pos)
	{
		SListPopBack(pphead);
	}
	else
	{
		SLN* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
	}
}
void SListDestroy(SLN** pphead)
{
	assert(*pphead != NULL);
	while(*pphead!=NULL)
	{
		SLN* next = (*pphead)->next;
		free(*pphead);
		*pphead = next;
	}
}



结语

说实话,感觉写的有点水,如果有什么好的提议欢迎讨论

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

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

相关文章

0115 作用域,对象

作用域意义&#xff1a;一段代码中所用到的名字不总是有效可用的&#xff0c;限定这个名字的可用性代码范围全局作用域全局有效&#xff0c;作用所有代码局部作用域局部有效&#xff0c;作用于函数内的代码环境&#xff0c;和函数有关也称函数作用域块级作用域在大括号{}有效&a…

Docker中安装Centos

window版的docker 1.cmd拉取centos镜像 docker pull centos2.启动centos容器&#xff0c;并把docker上centos的22端口映射到本机50001端口(端口号可以自己指定) 3.进入到Centos容器 通过docker命令,查看当前存在的镜像或者容器 查看镜像: docker images查看容器: docker …

js如何实现随机数切换

前言在一些电商网站,或一些活动页上,看到一些特效,比如:抽奖时,点击图片,实现图片的随机切换,数字的随机切换等,为了吸引用户的注意力,增加网页的互动性,这个效果是怎么实现的呢01具体示例https://coder.itclan.cn/fontend/js/14-click-num-suiji/点击文末左下角阅读原文,即可查…

MySQL三大日志(binlog、redo log和undo log)详解

MySQL三大日志binlog、redo log和undo log详解1.redo logredo log概述刷盘时机innodb_flush_log_at_trx_commit0innodb_flush_log_at_trx_commit1innodb_flush_log_at_trx_commit2日志文件组2.binlogbinlog 概述记录格式写入机制刷盘时机3.两阶段提交4.undo log5.总结1.redo lo…

SSL和TLS协议如何提供身份验证、机密性和完整性

SSL 和 TLS 协议使两方能够相互识别和验证&#xff0c;并以机密性和数据完整性进行通信。SSL 和 TLS 协议通过 Internet 提供通信安全性&#xff0c;并允许客户端/服务器应用程序以保密和可靠的方式进行通信。这些协议有两层&#xff1a;记录协议和握手协议&#xff0c;它们位于…

要命!我篡改了系统命令惊现事故,竟要扣我年终奖-Golang-cobra

打工还是要打工的。。我最后也没发出去。 紧急处理以后&#xff0c;现在写复盘&#xff0c;大家随我看看我到底是在学习哪些内容。 &#xff08;以上内容纯属虚构&#xff0c;如有雷同纯属巧合&#xff09; 简介 之前我们讲过pflag和os.Args&#xff0c;现在说说cobra这个命令行…

如何将SQL Server数据从表导出到CSV文件

在本文中,我们将使用四种不同的工具将表从SQL Server导出到.csv文件。此外,你将学习如何将带有或不带有头的SQL查询结果导出到.csv文件。 SQL Server数据从表导出到CSV文件 使用SQL Server Management Studio将SQL结果导出到具有或不具有标题的CSV文件一、不带标题二、带标题…

图形编辑器:图形和辅助线绘制的坐标问题

大家好&#xff0c;我是前端西瓜哥。今天看看绘制图形和辅助线时&#xff0c;坐标转换的一些注意点。 项目地址&#xff0c;欢迎 star&#xff1a; https://github.com/F-star/suika 线上体验&#xff1a; https://blog.fstars.wang/app/suika/ 先回顾一下之前讲的视口坐标和场…

docker镜像与容器实践

一、引子 镜像和容器是不同的概念&#xff0c;本文主要是为了通过实践来强化对这两种不同概念的理解。 二、安装docker 安装docker&#xff0c;执行以下命令即可&#xff1a; # 安装依赖 yum install -y yum-utils device-mapper-persistent-data lvm2 # 设置国内源 yum-co…

显示Linux系统上的服务

init 和 systemd 都是 Linux的 init 守护进程&#xff0c;systemd出现较晚&#xff0c;最近的 Linux 发行版中很常用。init 使用service命令管理服务&#xff0c;而Systemd用systemctl命令管理服务。init 和 systemd 都是 Linux的 init 守护进程&#xff0c;即使你的 Linux 系统…

Maven3.8.*系列 settings.xml详解

文章目录文末,拿完整Settings配置文件设置参考介绍简要概述设置详细信息简单的价值观插件组服务器密码加密的镜像代理配置文件激活性能库插件的储存库活动概况直达文末,拿完整Settings配置文件结语文末,拿完整Settings配置文件 设置参考 介绍 简要概述 的 settings 元素 set…

返乡上云图

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…

PicGo+Github搭建图床

文章目录一、Github仓库创建二、配置PicGo三、实际体验四、PicGo 2.3.1参考资料一、Github仓库创建 PS&#xff1a;它只会显示一次&#xff0c;所以最好把它复制下来到你的备忘录存好&#xff0c;方便下次使用&#xff0c;否则下次有需要重新新建&#xff1b; 二、配置PicGo …

Linux进程控制(进程退出+进程等待)

目录 一、子进程创建 1.1 fork函数深入 1.2 写时拷贝 二、进程退出 2.1.1 进程退出码概念 2.1.2 系统退出码文字描述 2.1.3 _exit和exit函数 2.1.4 查看退出码 三、进程等待 3.1 进程等待解决僵尸进程 3.2 进程等待方法 3.2.1 wait 3.2.2 waitpid(&#xff09; 四、…

seata部署指南(v1.6.1)

Seata 搭建 db模式版本 V1.6.1一、 简介二、下载三、建表&#xff08;仅db&#xff09;四、配置 seata server 参数4.1、V1.4.2之前方式4.2、V1.4.2 之后推荐方式(seataServer.properties)五、配置Server5.1、 修改 appplication.yml5.1.1、 修改 appplication.yml seata.store…

文件操作(File类)

文章目录一、初识文件二、File类构造方法常用方法一、初识文件 我们目前是如何存储数据的?弊端是什么? int a 1; int[] arr new int[5];我们这些数据是在内存中存储的&#xff0c;是不能够长久保存的。 那么&#xff0c;我们的计算机当中有没有一块硬件可以长久存储数据…

PostgreSQL(一)Windows安装

目录一、下载二、安装PostgreSQL三、安装StackBuilder四、打开PostgreSQL管理工具pgAdmin五、打开命令行一、下载 下载地址&#xff1a; https://www.enterprisedb.com/downloads/postgres-postgresql-downloads 下载后安装包如下&#xff1a; 二、安装PostgreSQL 双击打开安…

DataX使用入门

DataX 是阿里云 DataWorks数据集成 的开源版本&#xff0c;在阿里巴巴集团内被广泛使用的离线数据同步工具/平台。DataX 实现了包括 MySQL、Oracle、OceanBase、SqlServer、Postgre、HDFS、Hive、ADS、HBase、TableStore(OTS)、MaxCompute(ODPS)、Hologres、DRDS 等各种异构数据…

Java 日志框架 Log4J

文章目录引言什么是Log4JLog4J三大组件Log4J日志级别Log4J基本使用自定义配置文件Appender示例FileAppenderDailyRollingFileAppenderRollingFileAppenderJDBCAppender自定义Logger引言 Java 日志框架 JUL 在这篇文章中已经向大家介绍了我们为什么要使用日志文件、常见的日志…

张力调节(精密调节气阀应用)

跳舞轮对应张力调节范围&#xff0c;我们可以通过改变气缸的气压方式间接改变&#xff0c;张力跳舞轮在收放卷闭环控制上的详细应用&#xff0c;可以参看下面的文章链接&#xff0c;这里我们主要讨论精密可调气阀的模拟量编程问题。 PLC张力控制&#xff08;开环闭环算法分析&…