《数据结构学习笔记---第三篇》---单链表具体实现

news2024/11/18 3:34:54

目录

1.链表

1.1 链表的概念及结构

2.不带头单链表的实现

 2.1创建头文件“SList.h”

2.2 创建具体接口实现文件SList.c

2.2.1打印

2.2.2申请链表结点

2.2.3创建一个长度为n的链表

 2.2.4尾插尾删

2.2.5头插头删

2.2.6寻找x元素,返回pos

2.2.7插入和删除pos之后的位置

 2.2.8插入pos之前的位置和删除pos位置

2.2.9 销毁链表

3.主函数的实现



1.链表

1.1 链表的概念及结构

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

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

1. 单向、双向

2. 带头、不带头

3. 循环、非循环

虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结 构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向 循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而 简单了,后面我们代码实现了就知道了。

2.不带头单链表的实现

 2.1创建头文件“SList.h”

  ​​​​​为什么要创立头文件

#pragma once
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

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

SLTNode* BuySLTNode(SLTDataType);//申请结点

SLTNode* CreateSList(int n);//创建一个多长的链表

void SLTPrint(SLTNode* phead);

void SLTPushBack(SLTNode**pphead,SLTDataType x);
void SLTPopBack(SLTNode**pphead);
void SLTPushFront(SLTNode** pphead, SLTDataType x);
void SLTPopFront(SLTNode** pphead);

SLTNode* SListFind(SLTNode* plist, SLTDataType x);//找x的位置

void SListInsertAfter(SLTNode* pos, SLTDataType x);//插入pos之后的位置
void SListErasetAfter(SLTNode* pos);//删除pos后面的位置

void SListInsert(SLTNode**pphead,SLTNode* pos, SLTDataType); //插入pos之前的位置 
void SLTErase(SLTNode** pphead,SLTNode* pos);//删除pos

void SLTDestroy(SLTNode** pphead);//销毁链表

这里我们用来结构体的嵌套来定义单链表的节点结构体的嵌套定义

2.2 创建具体接口实现文件SList.c

先引用#include "SList.h"

2.2.1打印

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

 在这里我们没有断言assert,如果头指针为空指针,程序就会打印出NULL。

2.2.2申请链表结点

SLTNode* BuySLTNode(SLTDataType x)//申请结点
{
	SLTNode* newnode= (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("melloc fail");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

 在这里我们用了扩容,但我们使用的是malloc,和顺序表有所不同的是我们并不需要异地扩容,对于链表来说存储的位置本就是随机的,不需要整块连续的空间。

2.2.3创建一个长度为n的链表

SLTNode* CreateSList(int n)//创建一个多长的链表
{
	SLTNode* phead = NULL, * ptail = NULL;
	int x = 0;
	for (int i = 0 ; i < n; i++)
	{
		SLTNode* newnode = BuySLTNode(i);
		if (phead == NULL)
		{
			phead = ptail = newnode;
		}
		else
		{
			ptail->next = newnode;
			ptail = newnode;
		}
	}
	return phead;
}

 2.2.4尾插尾删

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




void SLTPopBack(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* tail = *pphead;
	SLTNode* prev = *pphead;
	if ((* pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else 
	{
		while (tail->next->next)
		{
			tail = tail->next;

		}
		free(tail->next);
		tail->next = NULL;
	}
}

 注意:

  • 我们可以看到我们这里与顺序表明显不同的是我们这里传入了二级指针,这到底是为什么,看这篇博文单链表尾插过程中为什么传入二级指针
  • 尾删时注意 free(tail)与 free(tail->next)的区别,其实原理和上述问题类似,因为后者修改的是结构体指针直接改变到了结构体,而仅仅free(tail)会导致出了函数的作用域,tail栈帧销毁,无法真正的改变到结构体

2.2.5头插头删

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

void SLTPopFront(SLTNode** pphead) {
	assert(*pphead);
	SLTNode* cur = *pphead;
	cur = cur->next;
	free(*pphead);
	*pphead = cur;

}

2.2.6寻找x元素,返回pos

SLTNode* SListFind(SLTNode* plist, SLTDataType x)
{
	assert(plist);
	SLTNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
	
	
}//找x的位置

2.2.7插入和删除pos之后的位置

void SListInsertAfter(SLTNode* pos, SLTDataType x)
{
	/*if (pos == NULL)
	{
		SLTPushBack(pos, x);
	}
	else {*/
	assert(pos);
		SLTNode*newnode=BuySLTNode(x);
		SLTNode* cur = pos;
		SLTNode* lnext = pos->next;
		cur->next = newnode;
		newnode->next = lnext;
	/*}*/
}//插入pos之后的位置

}

 2.2.8插入pos之前的位置和删除pos位置

void SListInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
	assert(pos);
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		SLTNode* newnode = BuySLTNode(x);
		/*while (prev->next)
		{
			if (prev->next == pos)
			{
				prev->next = newnode;
				newnode->next = pos;
			}
			prev = prev->next;
		}*/
		while (prev->next->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}

} //插入pos之前的位置
 
void SLTErase(SLTNode** pphead, SLTNode* pos)//删除pos
{
	assert(pos);//写错了
	if (pos== *pphead)
	{
		SLTPopFront(pphead);
	}
	
else {
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* lnext = pos;
		prev->next = prev->next->next;
		free(pos);
	}
}//删除pos

2.2.9 销毁链表

void SLTDestroy(SLTNode** pphead)
{
	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* tail = cur->next;
		free(cur);
		cur = tail;
		cur = cur->next;
	}
	*pphead = NULL;
}//销毁链表

注意置空,防止野指针,打印的时候没断言。

3.主函数的实现

#include "SList.h"
void SListTest1() {

	SLTNode* n1 = BuySLTNode(1);
	SLTNode* n2 = BuySLTNode(2);
	SLTNode* n3 = BuySLTNode(3);
	SLTNode* n4 = BuySLTNode(4);
	SLTNode* n5 = BuySLTNode(5);
	n1->next = n2;
	n2->next = n3;
	n3->next = n4;
	n4->next = n5;
	n5->next = NULL;
//	SLTNode*plist=CreateSList(5);
	SLTPrint(n1);
}
void SListTest2() {
	SLTNode* plist = CreateSList(1);
    SLTPrint(plist);
	/*SLTPushBack(&plist, 100);*/
	SLTPrint(plist);
	SLTPopBack(&plist);
	SLTPrint(plist);
	SLTPushFront(&plist,9);
	SLTPushFront(&plist,99);
	SLTPushFront(&plist,999);
	SLTPrint(plist);
	SLTPopFront(&plist);
	SLTPrint(plist);
}
void SListTest3() {
	SLTNode* plist = CreateSList(5);
	SLTPrint(plist);
	/*SLTNode*p=SListFind(plist, 3);
	SListInsertAfter(p, 100);
	SLTPrint(plist);
	SListErasetAfter(p);
	SLTPrint(plist);*/
	SLTNode*p = SListFind(plist, 0);
	/*SListInsert(&plist, p, 999);
	SLTPrint(plist);*/
	SLTErase(&plist, p);
	SLTPrint(plist);
	SLTDestroy(&plist);
	SLTPrint(plist);
}


int main()
{
	SListTest1();
	SListTest2();
	SListTest3();
	return 0;
}

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

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

相关文章

基于模糊控制算法的倒立摆控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 对倒立摆模型进行模糊控制器simulink建模&#xff0c;利用倒立摆的摆角角度与小车的位置来控制小车的推力&#xff0c;控制了倒立摆的摆角问题&#xff0c;使得小车最终停在稳…

【Linux】Ubuntu20.04解决网卡、显卡驱动不正确的问题

文章目录 1、概述2、问题描述2.1、快捷栏无无线设置2.2、设置中无Wifi设置专栏2.3、接入外接屏幕无作用 3、网卡驱动解决方案3.1、在18.04的旧方法3.1.1、安装源更换3.1.1.1、备份原始安装源3.1.1.2、修改安装源地址3.1.1.3、更新源地址 3.1.2、安装依赖3.1.3、安装编译器3.1.3…

大模型精准度提升调研

如何让ChatGPT更靠谱 1. 预训练大模型概述 关于预训练 预训练&#xff08;Pre-training&#xff09;是深度学习中一种常见的技术&#xff0c;特别是在自然语言处理&#xff08;NLP&#xff09;和计算机视觉&#xff08;CV&#xff09;等领域中。它通常指在一个大型的、通常是…

智能小程序有哪些重要能力?

概念 小程序能力是模块化的&#xff0c;它以kit的形式提供给业务&#xff08;开发者&#xff09;。通过kit可以实现快速接入涂鸦生态&#xff0c;获得互联互通的能力。 能力分包 能力分类包名基础能力BaseKit小程序容器能力MiniKit涂鸦内部基础能力以及细粒度通用业务能力Biz…

服务器监控软件夜莺采集监控(三)

文章目录 一、采集器插件1. exec插件2. rabbitmq插件3. elasticsearch插件 二、监控仪表盘1. 系统信息2. 数据服务3. NginxMQ4. Docker5. 业务日志 一、采集器插件 1. exec插件 input.exec/exec.toml [[instances]] commands ["/home/monitor/categraf/scripts/*.sh&q…

区块链食品溯源案例实现(一)

引言&#xff1a; 食品安全问题一直是社会关注的热点&#xff0c;而食品溯源作为解决食品安全问题的重要手段&#xff0c;其重要性不言而喻。传统的食品溯源系统往往存在数据易被篡改、信息不透明等问题&#xff0c;而区块链技术的引入&#xff0c;为食品溯源带来了革命性的变革…

第十篇【传奇开心果系列】Python自动化办公库技术点案例示例:深度解读Python自动化操作Excel

传奇开心果博文系列 系列博文目录Python自动化办公库技术点案例示例系列博文目录 前言一、重要作用解说二、Python操作Excel的常用库介绍三、数据处理和分析示例代码四、自动化报表生成示例代码五、数据导入和导出示例代码六、数据可视化示例代码八、数据校验和清洗示例代码九、…

数据结构——第5章 树和二叉树

1 二叉树 二叉树和树都属于树形结构&#xff0c;但两者互不包含。即二叉树不是特殊的树。 1.1 二叉树的基本概念 1.2 二叉树的顺序存储 仅适用于完全二叉树 #define MaxSize 100 typedef int ElemType; typedef struct TreeNode{ElemType value;//结点中的数据元素bool isE…

【面试】Elasticsearch 在部署时,对 Linux 的设置有哪些优化方法?

Elasticsearch 在部署时&#xff0c;对 Linux 的设置有哪些优化方法&#xff1f; Elasticsearch是一个分布式搜索和分析引擎&#xff0c;它在Linux环境下的性能和稳定性可以通过一些优化方法进行提升。以下是一些针对Linux环境下Elasticsearch部署的优化方法&#xff1a; 1. 内…

OSG编程指南<二十一>:OSG视图与相机视点更新设置及OSG宽屏变形

1、概述 什么是视图?在《OpenGL 编程指南》中有下面的比喻,从笔者开始学习图形学就影响深刻,相信对读者学习场景管理也会非常有帮助。 产生目标场景视图的变换过程类似于用相机进行拍照,主要有如下的步骤: (1)把照相机固定在三脚架上,让它对准场景(视图变换)。 (2)…

spring安全框架之Shiro

Shiro 一、现存问题 1.1 现存问题 认证&#xff08;登录&#xff09;&#xff1a;认证操作流程都差不多&#xff0c;但是每次都需要手动的基于业务代码去实现&#xff0c;很麻烦&#xff01; 授权&#xff1a;如果权限控制粒度比较粗&#xff0c;可以自身去实现&#xff0c…

算法打卡day19

今日任务&#xff1a; 1&#xff09;235. 二叉搜索树的最近公共祖先 2&#xff09;701.二叉搜索树中的插入操作 3&#xff09;450.删除二叉搜索树中的节点 235. 二叉搜索树的最近公共祖先 题目链接&#xff1a;235. 二叉搜索树的最近公共祖先 - 力扣&#xff08;LeetCode&…

Mysql数据库——高级SQL语句补充

目录 一、子查询——Subquery 1.环境准备 2.In——查询已知的值的数据记录 2.1子查询——Insert 2.2子查询——Update 2.3子查询——Delete 3.Not In——表示否定&#xff0c;不在子查询的结果集里 3.Exists——判断查询结果集是否为空 4.子查询——别名 二、视图—…

政安晨:【Keras机器学习实践要点】(六)—— 使用内置方法进行训练和评估

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras实战演绎机器学习 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 本文涵盖使用内置 API 进行训练和验证&#…

pytorch+tensorboard

安装依赖 pip install teorboard pip install torch_tb_profiler了解teorboard 记录并可视化标量[组]、图片[组]。 如何使用 第一步&#xff1a;构建模型&#xff0c;记录中间值&#xff0c;写入summarywriter 每次写入一个标量add_scalar 比如&#xff1a; from torch.u…

深度学习:基于PyTorch的模型解释工具Captum

深度学习&#xff1a;基于PyTorch的模型解释工具Captum 引言简介示例安装解释模型的预测解释文本模型情绪分析问答 解释视觉模型特征分析特征消融鲁棒性 解释多模态模型 引言 当我们训练神经网络模型时&#xff0c;我们通常只关注模型的整体性能&#xff0c;例如准确率或损失函…

上位机图像处理和嵌入式模块部署(qmacvisual区域提取)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在图像处理中&#xff0c;有两部分比较重要&#xff0c;一个是区域分割&#xff0c;一个是区域提取。区域分割&#xff0c;比较好理解&#xff0c;…

Chrome 插件 storage API 解析

Chrome.storage API 解析 使用 chrome.storage API 存储、检索和跟踪用户数据的更改 一、各模块中的 chrome.storage 内容 1. Service worker 中 runtime 内容 2. Action 中 runtime 内容 3. Content 中 runtime 内容 二、权限&#xff08;Permissions&#xff09; 如果需使…

SPU赋能PSI:探秘隐私集合求交核心技术与高级调度架构实践

1.SPU实现的PSI介绍 1.PSI的定义和种类 隐私集合求交&#xff08;Private Set Intersection, PSI&#xff09;是一种在密码学和安全多方计算&#xff08;MPC&#xff09;领域中的关键技术&#xff0c;允许两个或多个参与者在不泄露各自输入集合中非交集部分的前提下&#xff…

【python分析实战】成本:揭示电商平台月度开支与成本结构占比 - 过于详细 【收藏】

重点关注本文思路&#xff0c;用python分析&#xff0c;方便大家实验复现&#xff0c;代码每次都用全量的&#xff0c;其他工具自行选择。 全文3000字&#xff0c;阅读10min&#xff0c;操作1小时 企业案例实战欢迎关注专栏 每日更新&#xff1a;https://blog.csdn.net/cciehl/…