链表的实现(文末附完整代码)

news2024/11/18 5:56:53

链表的概念及结构

链表是一种物理存储结构上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的
我们在上一篇文章所学习的顺序表是连续存储的
例如:
顺序表就好比火车上的一排座位,是连续的
而链表就好比是火车的各节车厢,中间有东西将其互相连接的
在这里插入图片描述
链表的基本结构图如下:
有一个指针指向下一个节点
在这里插入图片描述

链表的概念及结构

实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:
链表可以是单向和双向,循环和不循环,带头和不带头,这样一组合,就会出现八种类型的列表
单向的列表如下:
在这里插入图片描述
双向列表:
相比较单向,双向的增删查改较为容易,他会自带一个prev的节点,能顾标记当前节点的前一个节点
在这里插入图片描述
循环列表:
其实循环列表就是最后一个节点指向了开头节点
在这里插入图片描述
带头和不带头:
头节点我们可以称其为哨兵位,它是不会在链表中存储有效的数据的
在这里插入图片描述
其实八种链表我们最常用的只有两种:
无头单向非循环链表:
在这里插入图片描述
带头双向循环列表:
在这里插入图片描述

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是后面的学习中你会发现其实他是比较简单的

链表的实现

首先我们要了解的就是单链表的实现:
头文件如下:

#include<stdio.h>
#include<assert.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表删除pos位置之后的值
void SListEraseAfter(SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** plist);

新节点的创建:
我们要创建一个节点,首先就是要为他动态开辟一个空间
然后再将这个新节点newnode的值赋予x,并且将他的next置为空,然后函数返回这个节点

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

链表的打印:
打印链表是我们可以用一个符号->来代替空格,但是链表实际上是没有这个符号的
我们可以首先定义cur,从链表的第一个节点开始遍历,知道cur为空时,就不会打印了,并且打印一次cur的data,cur要等于cue的next

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

单链表的尾插:
这里我们要注意的是要记得用二级指针,因为当链表为空时,我们要改变的是节点的地址,而我们要改变地址,就要用地址的地址,也就是二级指针
首先,需要插入一个节点我们要做的就是创建一个新节点,我们之前定义了的一个函数直接使用
然后我们创建一个tail指针,让他从链表的第一个位置开始遍历,一直遍历到最后一个节点,此时令tail的next等于newnode即可

void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	/*assert(*pplist);*///链表为空时可以尾插
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

单链表的头插:
头插比较简单,我们直接将新节点的next等于链表的第一个节点即可,也就是*pplist,我们传进来的是**pplist,是节点地址的地址,所以我们需要解引用一次才能将地址给newnode的next

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

单链表的尾删:
尾删的情况我们要分为两种:
1.只有一个节点时:
只有一个节点时我们直接free掉这个节点,其次为了防止野指针,我们要将其置空
2.当有多个节点时:
我们创建一个tail和prev,然后用循环将tail遍历到最后一个节点,循环的终止条件时tail->next为空,条件满足时就将tail赋予prev,当跳出循环时,prev就是尾节点的前一个节点,我们直接将tail给free掉,将其置空,这样尾节点就被删除了
在这里插入图片描述

void SListPopBack(SListNode** pplist)
{
	assert(*pplist);//链表为空时不能再删,暴力检查 
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* prev = NULL;
		SListNode* tail = *pplist;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

单链表的头删:
头删就很简单了,首先定义一个tail存入第一个节点的位置,然后将第一个节点的位置移动到他的next,注意记得将临时存储第一个节点位置的指针给free掉,避免出现野指针的问题

void SListPopFront(SListNode** pplist)
{
	assert(*pplist);//链表为空时不能再删,暴力检查
	SListNode* tail = *pplist;
	*pplist = (tail)->next;
	free(tail);
}

节点的查找:
节点的查找也比较容易,直接用while循环遍历,如果cur的data和x相等,就返回cur,否则就是没有这个节点,返回null

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur->next)
	{
		if (cur->data == x)
		{
			return cur;
			//break;
		}
		cur = cur->next;
	}
	return NULL;
}

在一个节点之后插入节点:
需要插入节点首先要做的就是创建一个新节点
然后首先要做的是将newnode的next连接到要插入位置pos的next
再将pos的next置为newnode
这里注意,位置不能颠倒,不然newnode的next就找不到了
在这里插入图片描述

void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

删除一个节点之后的节点:
要删除节点之后的一个节点,首先这个节点之后一定要有一个节点,所以要对pos->next进行断言
我们将pos->next存储到next中,然后将pos->next直接指向next->next即可,也就是把pos->next=pos->next->next
在这里插入图片描述

void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);
	//pos->next = pos->next->next;
	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

单链表的销毁:
直接将pplist置空即可

void SListDestroy(SListNode** pplist)
{
	SListNode* node = *pplist;
	node = NULL;
	free(node);
}

好了,今天的分享到这里就结束了,感谢大家的支持!
完整代码如下:

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc");
		return NULL;
	}
	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, SLTDateType x)
{
	assert(pplist);
	/*assert(*pplist);*///链表为空时可以尾插
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

void SListPopBack(SListNode** pplist)
{
	assert(*pplist);//链表为空时不能再删,暴力检查 
	//SListNode* tail = pplist;
	//while (tail->next->next)
	//{
	//	tail = tail->next;
	//}
	//free(tail->next);
	//tail->next = NULL;
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* prev = NULL;
		SListNode* tail = *pplist;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}

void SListPopFront(SListNode** pplist)
{
	assert(*pplist);//链表为空时不能再删,暴力检查
	SListNode* tail = *pplist;
	*pplist = (tail)->next;
	free(tail);
}

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur->next)
	{
		if (cur->data == x)
		{
			return cur;
			//break;
		}
		cur = cur->next;
	}
	return NULL;
}

void SListInsertAfter(SListNode* pos, SLTDateType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}


void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);
	//pos->next = pos->next->next;
	SListNode* next = pos->next;
	pos->next = next->next;
	free(next);
}

void SListDestroy(SListNode** pplist)
{
	SListNode* node = *pplist;
	node = NULL;
	free(node);

}

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

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

相关文章

字节面试:请说一下DDD的流程,用电商系统为场景

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业字节、如阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 谈谈你的DDD落地经验&#xff1f; 谈谈你对DDD的理解&…

matlab GUI界面实现ZieglerNicholas调节PID参数

1、内容简介 略 11-可以交流、咨询、答疑 ZieglerNicholas、PID、GUI 2、内容说明 GUI界面实现ZieglerNicholas调节PID参数 通过ZieglerNicholas调节PID参数&#xff0c;设计了GUI 3、仿真分析 略 4、参考论文 略 链接&#xff1a;https://pan.baidu.com/s/1yQ1yDfk-_…

vue+mongodb+nodejs实现表单增删改查

ExpressMongodbVue实现增删改查 效果图 前言 最近一直想学下node,毕竟会node的前端更有市场。但是光看不练&#xff0c;感觉还是少了点什么&#xff0c;就去github上看别人写的项目&#xff0c;收获颇丰&#xff0c;于是准备自己照葫芦画瓢写一个。 作为程序员&#xff0c;一…

电容的作用

文章目录 总结1.降压2.滤波3.延时4.耦合5.旁路电容 总结 1.降压 问题&#xff1a; 直接连接灯泡会烧掉 解决方案 进一步为了防止电容放电&#xff0c;伤人&#xff0c;加入一个大电阻 2.滤波 直流的情况 交流的情况 频率与容抗的关系 3.延时 4.耦合 滤除直流成分&#xf…

学习c#的第七天

目录 C# 封装 概念 Public 访问修饰符 Private 访问修饰符 Protected 访问修饰符 Internal 访问修饰符 Protected Internal 访问修饰符 总结 C# 封装 概念 在面向对象程序设计中&#xff0c;封装是一种将数据和方法包含在一个单元中&#xff0c;并控制对这些数据和方…

LeetCode算法心得——高级访客(模拟枚举+小窗口)

大家好&#xff0c;我是晴天学长&#xff0c;今天的周赛第二题&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .高级访客 给你一个长度为 n 、下标从 0 开始的二维字符串数组 access_times 。对于每个 …

Redis五种数据类型及命令操作(二)

&#x1f388;个人公众号:&#x1f388; :✨✨✨ 可为编程✨ &#x1f35f;&#x1f35f; &#x1f511;个人信条:&#x1f511; 知足知不足 有为有不为 为与不为皆为可为&#x1f335; &#x1f349;本篇简介:&#x1f349; 本篇记录Redis五种数据类型及命令操作&#xff0c;如…

Oracle(2-1) Networking Overview

文章目录 一、基础知识1、Network Environ Challenges 网络环境挑战2、Simple Network :2-Tier 简单的两层网络3、Simple to Complex : N-Tier 简单到复杂&#xff1a;N层网络4、Oracle Network Solutions Oracle网络解决方案5、Key Features of Oracle Net Oracle Net的主要功…

[MySQL] MySQL表的基础操作

文章目录 一、创建表 1、1 SQL语法 1、2 实例演示 二、查询表 三、修改表 3、1 修改表名字 3、2 新增列&#xff08;字段&#xff09; 3、3 修改列类型 3、4 修改列名 3、5 删除表 四、总结 &#x1f64b;‍♂️ 作者&#xff1a;Ggggggtm &#x1f64b;‍♂️ &#x1f440; 专…

详解Redis持久化(上篇——RDB持久化)

Redis持久化的作用和意义 Redis 持久化是一种机制&#xff0c;用于将内存中的数据写入磁盘&#xff0c;以保证数据在服务器重启时不会丢失。持久化是为了解决内存数据库&#xff08;如 Redis&#xff09;在服务器关闭后&#xff0c;数据丢失的问题。 Redis 持久化的主要作用和…

如何判断一个角是否大于180度(2)

理论计算见上一篇&#xff1a; 如何判断一个角是否大于180度&#xff1f;_kv1830的博客-CSDN博客 此篇为代码实现 一。直接上代码&#xff1a; import cv2 as cv import numpy as np import mathdef get_vector(p_from, p_to):return p_to[0] - p_from[0], p_to[1] - p_from…

【第六章】软件设计师 之 数据结构与算法基础

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、数据结构与算法基础 2、数据 3、稀疏矩阵…

【第三章】软件设计师 之 数据库系统

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 1、数据库系统前言 2、三级模式 - 两级映射…

爬虫项目(12):正则、多线程抓取腾讯动漫,Flask展示数据

文章目录 书籍推荐正则抓取腾讯动漫数据Flask展示数据 书籍推荐 如果你对Python网络爬虫感兴趣&#xff0c;强烈推荐你阅读《Python网络爬虫入门到实战》。这本书详细介绍了Python网络爬虫的基础知识和高级技巧&#xff0c;是每位爬虫开发者的必读之作。详细介绍见&#x1f44…

Netty入门指南之NIO Selector写操作

作者简介&#xff1a;☕️大家好&#xff0c;我是Aomsir&#xff0c;一个爱折腾的开发者&#xff01; 个人主页&#xff1a;Aomsir_Spring5应用专栏,Netty应用专栏,RPC应用专栏-CSDN博客 当前专栏&#xff1a;Netty应用专栏_Aomsir的博客-CSDN博客 文章目录 参考文献前言操作演…

transfomer模型——简介,代码实现,重要模块解读,源码,官方

一、什么是transfomer Transformer是一种基于注意力机制&#xff08;attention mechanism&#xff09;的神经网络架构&#xff0c;最初由Vaswani等人在论文《Attention Is All You Need》中提出。它在自然语言处理&#xff08;NLP&#xff09;领域取得了巨大成功&#xff0c;特…

自定义Graph Component:1.1-JiebaTokenizer具体实现

JiebaTokenizer类继承自Tokenizer类&#xff0c;而Tokenizer类又继承自GraphComponent类&#xff0c;GraphComponent类继承自ABC类&#xff08;抽象基类&#xff09;。本文使用《使用ResponseSelector实现校园招聘FAQ机器人》中的例子&#xff0c;主要详解介绍JiebaTokenizer类…

JavaScript_动态表格_添加功能

1、动态表格_添加功能.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>动态表格_添加功能</title><style>table{border: 1px solid;margin: auto;width: 100%;}td,th{text-align: ce…

Python:Unittest框架快速入门:用例、断言、夹具、套件、HTML报告、ddt数据驱动

快速看了套Unittest的入门教程 软件测试全套资料赠送_哔哩哔哩_bilibili软件测试全套资料赠送是快速入门unittest测试框架&#xff01;全实战详细教学&#xff0c;仅此一套&#xff01;的第1集视频&#xff0c;该合集共计11集&#xff0c;视频收藏或关注UP主&#xff0c;及时了…

ElasticSearch学习和使用 (使用head软件可视化es数据)

使用步骤 直接使用 Elasticsearch的安装和使用 下载Elasticsearch6.2.2的zip包&#xff0c;并解压到指定目录&#xff0c;下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-2-2运行bin目录下的elasticsearch.bat启动Elasticsearch安…