数据结构:单链表的实现(C语言)

news2024/11/17 22:20:16

在这里插入图片描述

个人主页 : 水月梦镜花
个人专栏 : 《C语言》 《数据结构》

文章目录

  • 前言
  • 一、单链表实现思路和图解
    • 1.节点的定义(SListNode)
    • 2.申请一个节点(BuySListNode)
    • 3.单链表打印(SListPrint)
    • 4.单链表尾插(SListPushBack)
    • 5.单链表的头插(SListPushFront)
    • 6.单链表的尾删(SListPopBack)
    • 7.单链表头删(SListPopFront)
    • 8.单链表的查找(SListFind)
    • 9.单链表在pos位置之后插入x(SListInsertAfter)
    • 10.单链表删除在pos位置之后的值(SListEraseAfter)
    • 11.单链表的销毁(SListDestroy)
  • 二、代码实现
  • 总结


前言

本博客将要实现的无头单向不循环链表。


一、单链表实现思路和图解

1.节点的定义(SListNode)

我们将节点定义为如下结构:
在这里插入图片描述
其成员变量有data,next。

将int重命名为STLDataType,方便我们以后修改数据域的内容。

//无头单向不循环链表
typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SListNode;

2.申请一个节点(BuySListNode)

动态申明一个空间,来放置数据。如下:
在这里插入图片描述
将data的内容置成形参x,next置NULL。

//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	retur

3.单链表打印(SListPrint)

循环遍历链表,直到尾节点。创建一个结构体指针变量cur,循环cur = cur->next,并打印cur->data的内容,直到cur == NULL。
在这里插入图片描述

//单链表打印
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

4.单链表尾插(SListPushBack)

  • 如果链表不为NULL(链表中有元素),要先遍历链表找到尾节点,在让尾节点指向新节点,完成尾插。
  • 如果链表为NULL(链表中没有元素),此时应该直接让新节点等于头结点,完成尾插。(本链表是无哨兵位的)
  • 如果传入的头结点无效,直接判错。

在这里插入图片描述

在这里插入图片描述
当链表为NULL,我们就要修改头结点本身的内容,所以我们需要头结点的指针,而本文中头结点本身就是结构体指针,所以尾插函数参数我们需要二级指针。

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	assert(pplist);

	SListNode* newnode =  BuySListNode(x);
	if (*pplist != NULL)
	{
		SListNode* cur = *pplist;
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
	else
	{
		*pplist = newnode;
	}
}

5.单链表的头插(SListPushFront)

对于头插而言,链表是否有元素并不重要,我们只需要让新节点的指向头结点,并将头结点等于新节点。

在这里插入图片描述
因为头插链表,一定会改变头结点的内容,所以我们头插函数的形参也是二级指针。

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	
	newnode->next = *pplist;
	*pplist = newnode;
}

6.单链表的尾删(SListPopBack)

  • 如果链表元素有两个及两以上,我们需要两个指针变量prev,next来找尾节点,循环prev = cur,cur = cur->next,使next指向尾节点,prev指向尾节点的前一个,free尾节点,prev指向的节点指向NULL。
  • 如果链表元素只有一个,直接free头结点,使头结点置NULL。
  • 如果链表没有元素,直接判错。

在这里插入图片描述

在这里插入图片描述
当链表元素只有一个时,此时尾删链表,要修改头结点的内容,尾删函数的形参需要二级指针。

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	//链表为NULL
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* cur = *pplist;
		SListNode* prev = NULL;

		while (cur->next != NULL)
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = NULL;
		free(cur);
	}
}

7.单链表头删(SListPopFront)

我们需要一个结构体指针变量next,来保存头结点的下一个节点,然后free头结点,使头结点 = 指针变量next。

  • 如果链表没有元素,直接判错。

在这里插入图片描述

//单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	SListNode* next = (*pplist)->next;
	free(*pplist);
	*pplist = next;
}

8.单链表的查找(SListFind)

我们需要一个结构体指针变量cur,来遍历链表比较cur->data == x,如果相等,放回此时cur的内容(该节点的地址)。如果遍历完链表,并没有相等元素,放回NULL。

//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

9.单链表在pos位置之后插入x(SListInsertAfter)

我们让newnode指向pos下一个的节点,pos指向newnode。

  • 如果我们先让pos指向newnode,newnode在指向pos的下一个节点,就会造成newnode指向自己,导致链表成环。
  • 该函数不会影响头结点的内容,所以函数的形参不用二级指针。

在这里插入图片描述


//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

10.单链表删除在pos位置之后的值(SListEraseAfter)

我们需要一个结构体指针变量next,记录pos下一个节点的地址,然后是pos指向next的下一个节点,接着free(next)。

  • 如果链表只有一个元素,我们不能调用该函数,否则会导致NULL指针的解引用。
  • 该函数不会改变头结点的内容,所以形参我们不用二级指针。

在这里插入图片描述


//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos && pos->next);

	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

11.单链表的销毁(SListDestroy)

我们需要两个结构体指针prev,cur。先让prev = cur ,cur再指向下一个节点,free(prev),重复上述操作,直到cur指向NULL。

在这里插入图片描述
需要我们在函数调用完后,自己对头结点置NULL。

//单链表的销毁
void SListDestroy(SListNode* plist)
{
	assert(plist);

	SListNode* cur = plist;
	while (cur != NULL)
	{
		SListNode* prev = cur;
		cur = cur->next;
		free(prev);
	}
}

二、代码实现

//slist.h  文件


#pragma once
#define _CRT_SECURE_NO_WARNINGS 1

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

//无头单向不循环链表
typedef int SLTDataType;

typedef struct SListNode
{
	SLTDataType data;
	struct SListNode* next;
}SListNode;


//申请一个节点
SListNode* BuySListNode(SLTDataType x);

//单链表打印
void SListPrint(SListNode* plist);

//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x);

//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x);

//单链表的尾删
void SListPopBack(SListNode** pplist);

//单链表头删
void SListPopFront(SListNode** pplist);

//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x);

//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x);

//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos);

//单链表的销毁
void SListDestroy(SListNode* plist);
//slist.c    文件


#include "slist.h"

//申请一个节点
SListNode* BuySListNode(SLTDataType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


//单链表打印
void SListPrint(SListNode* plist)
{
	SListNode* cur = plist;

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}


//单链表尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	assert(pplist);

	SListNode* newnode =  BuySListNode(x);
	if (*pplist != NULL)
	{
		SListNode* cur = *pplist;
		while (cur->next != NULL)
		{
			cur = cur->next;
		}
		cur->next = newnode;
	}
	else
	{
		*pplist = newnode;
	}
}


//单链表的头插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	
	newnode->next = *pplist;
	*pplist = newnode;
}

//单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	//链表为NULL
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* cur = *pplist;
		SListNode* prev = NULL;

		while (cur->next != NULL)
		{
			prev = cur;
			cur = cur->next;
		}
		prev->next = NULL;
		free(cur);
	}
}


//单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	SListNode* next = (*pplist)->next;
	free(*pplist);
	*pplist = next;
}


//单链表的查找
SListNode* SListFind(SListNode* plist, SLTDataType x)
{
	SListNode* cur = plist;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}

//单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}


//单链表删除在pos位置之后的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos && pos->next);

	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}


//单链表的销毁
void SListDestroy(SListNode* plist)
{
	assert(plist);

	SListNode* cur = plist;
	while (cur != NULL)
	{
		SListNode* prev = cur;
		cur = cur->next;
		free(prev);
	}
}

总结

以上就是我对于无头单向不循环链表的实现!!!

在这里插入图片描述

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

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

相关文章

【ChatGPT辅助学Rust | 基础系列 | Hello, Rust】编写并运行第一个Rust程序

文章目录 前言一&#xff0c;创建项目二&#xff0c;两种编译方式1. 使用rustc编译器编译2. 使用Cargo编译 总结 前言 在开始学习任何一门新的编程语言时&#xff0c;都会从编写一个简单的 “Hello, World!” 程序开始。在这一章节中&#xff0c;将会介绍如何在Rust中编写并运…

音频编辑必备技能:怎么将音频转换mp3

丽萨&#xff1a;嘿&#xff0c;听说你最近在研究音频格式转换的方法&#xff0c;有眉目了吗&#xff1f; 凯瑞&#xff1a;没错&#xff0c;我下载了很多高清音乐&#xff0c;发现有些格式的音频文件在我的播放器上打不开&#xff0c;所以想一个转换工具。但是网上软件太多&a…

使用腾讯云 Cloud studio 实现调度百度AI实现文字识别

文章目录 前言导入模块设置百度AI的APP_ID、API_KEY和SECRET_KEY定义路径和文件列表打开文本文件准备写入数据逐个处理图片文件关闭文本文件重复处理其他图片文件完整代码解释说明 运行效果 前言 今天我们也来高大上一下&#xff0c;玩一把人工智能。那就是免费调用百度AI实现图…

站外引流效果差?一文带你搞懂解海外主流社交媒体算法!

在流量成本越来越高的当下&#xff0c;无论是平台卖家还是独立站卖家都在努力拓展流量渠道。站外引流是推动业务增长的关键策略&#xff0c;很多卖家会把重点放在内容营销上&#xff0c;但其实除了做好内容之前&#xff0c;了解社交媒体的算法才能让营销效果最大化。 01.Faceb…

操作系统专栏2进程管理from 小林coding

进程管理 基本概念进程控制进程上下文切换 线程进程和线程的比较进程通信管道消息队列共享内存信号量信号socket 基本概念 进程:一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元.并行和并发:状态: 其中挂起是指没有给程序分配实际…

一百三十八、ClickHouse——使用clickhouse-backup备份ClickHouse库表

一、目标 使用clickhouse-backup在本地全库备份ClickHouse的数据库 二、前提 已经安装好clickhouse-backup 注意&#xff1a;由于之前同事已经按照好clickhouse-backup&#xff0c;所以我就没有安装 如有需要请参考其他人的博客安装一下&#xff0c;下面是我认为比较好的一…

如何看待前端已死这个问题(大学生篇)

小编刚大学毕业&#xff0c;还记得是大三的时候选择的前端开发方向&#xff0c;那个时候行情其实并没有这么差&#xff0c;最近互联网上讨论这一个很火的话题&#xff0c;叫前端已死。那么我就说说我的看法吧&#xff0c;虽然可能比起行业的大佬会比较短浅&#xff0c;但我想就…

盘点12个Vue 3的高颜值UI组件库

今天给大家盘点12个Vue 3的高颜值UI组件库&#xff0c;凡是用过Vue 框架开发项目的老铁们最少有用过其中一种或者二种以上的UI组件库&#xff0c;用广东话讲&#xff1a;个个都靓。 今天给大家盘点12个Vue 3的高颜值UI组件库&#xff0c;凡是用过Vue 框架开发项目的老铁们最少有…

【我们一起60天准备考研算法面试(大全)-第三十天 30/60】【矩阵翻转】【矩阵相乘】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

一文读懂原生应用与混合应用

大家对于原生应用和混合应用已经非常熟悉了&#xff0c;这里就不再进行详细的介绍&#xff0c;用通俗易懂的话解释下他们的一些特点。 1、原生应用 在 Android、iOS 等移动平台上利用提供的开发语言、开发类库、开发工具进行 App 软件开发。比如 Android 是用 Java、Eclipse、…

日撸代码300行:第59天(数值型数据的 NB 算法)

代码来自闵老师”日撸 Java 三百行&#xff08;51-60天&#xff09;“&#xff0c;链接&#xff1a;https://blog.csdn.net/minfanphd/article/details/116975957 相较于符号型数据&#xff0c;数值型是将实例的概率密度带入进行概率计算。数值型数据仅需要对决策属性那里进行…

Vue 基础语法(二)

一、背景&#xff1a; 我们对于基础语法&#xff0c;说白了就是实现元素赋值&#xff0c;循环&#xff0c;判断&#xff0c;以及事件响应即可&#xff01; 二、v-bind 我们已经成功创建了第一个 Vue 应用&#xff01;看起来这跟渲染一个字符串模板非常类似&#xff0c;但是 V…

性能测试/负载测试/压力测试之间的区别

做测试一年多来&#xff0c;虽然平时的工作都能很好的完成&#xff0c;但最近突然发现自己在关于测试的整体知识体系上面的了解很是欠缺&#xff0c;所以&#xff0c;在工作之余也做了一些测试方面的知识的补充。不足之处&#xff0c;还请大家多多交流&#xff0c;互相学习。 …

全网最细,性能测试-接口压测 Locust固件实战,从0到1进阶...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 python如何进行性…

基于YOLOv5的S弯识别

基于YOLOv5的S弯识别 目录 基于YOLOv5的S弯识别技术背景算法介绍具体实现1、下载仓库2、配置环境3、数据处理4、转成engine文件5、使用代码实现识别 技术总结 技术背景 S弯识别是一个在自动驾驶和机器人领域中很常见的任务&#xff0c;它需要识别道路上的弯道&#xff0c;特别…

springboot+mybatis-flex初体验

mybatis作为一款应用非常广泛的持久层框架&#xff0c;随之又出现了mybatis的增强框架。mybatis的增强框架就是在mybatis 的基础上又添加了许多的功能&#xff0c;目的就是为简化开发&#xff0c;提供效率而生。mybatis-plus就是一款很流行的增加框架&#xff0c;在 mybatis-pl…

小程序学习(四):WXML模板语法

WXML模板语法-数据绑定 1.数据绑定的基本原则 ①在data中定义数据 ②在WXML中使用数据 2.动态绑定属性 WXML模板语法-事件绑定 3.什么是事件 4.小程序中常用的事件 5.事件对象的属性列表 6.target和currentTarget的区别 7.bindtap的语法格式 8.在事件处理函数中为data中的数据…

华为、阿里巴巴、字节跳动 100+ Python 面试问题总结(六)

系列文章目录 个人简介&#xff1a;机电专业在读研究生&#xff0c;CSDN内容合伙人&#xff0c;博主个人首页 Python面试专栏&#xff1a;《Python面试》此专栏面向准备面试的2024届毕业生。欢迎阅读&#xff0c;一起进步&#xff01;&#x1f31f;&#x1f31f;&#x1f31f; …

校园跑腿小程序功能分享

提起校园跑腿小程序大家都不陌生&#xff0c;尤其是对上大学的伙伴们来说,更是熟悉得不能再熟悉了&#xff0c;和我们的生活息息相关&#xff0c;密不可分。 对于现在的年轻人来说&#xff0c;网购是非常简单和方便的一种购物方式&#xff0c;随之快递也会越来越多。在我们国家…

rancher2使用helm部署harbor,搭建私有镜像/helm仓库

接上一篇《rancher2安装nfs-subdir-external-provisioner为PVC/PV动态提供存储空间&#xff08;动态分配卷&#xff09;》 本篇开始讲如何在rancher2中部署harbor&#xff0c;来搭建一个私有的镜像/helm仓库。 一、安装harbor服务 1. 在目标集群中添加命名空间 2. 将提供harb…