【数据结构】链表简介及单链表的实现

news2024/11/26 16:54:36

简单不先于复杂,而是在复杂之后。

在这里插入图片描述

文章目录

    • 1. 链表
      • 1.1 链表的概念及结构
      • 1.2 链表的分类
      • 1.3 无头单向非循环链表的实现

1. 链表

1.1 链表的概念及结构

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

在这里插入图片描述

在这里插入图片描述

现实中 数据结构中

在这里插入图片描述

1.2 链表的分类

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

  1. 单向或双向

在这里插入图片描述

  1. 带头或不带头:

在这里插入图片描述

  1. 循环或非循环:

在这里插入图片描述

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

在这里插入图片描述

  1. 无头单项非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单。

1.3 无头单向非循环链表的实现

Slist.h

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

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

//以下三种形式等价
//SLTNode*
//PSLTNode
//struct SlistNode

//动态申请一个结点
SLTNode* BuySLTNode(SLTDataType x);

//打印链表
void SListPrint(SLTNode* phead);
//void SListPrint(PSLTNode phead);

//销毁链表
//不及时销毁链表是一种内存泄露
void SListDestory(SLTNode** pphead);

//头插
void SListPushFront(SLTNode** pphead, SLTDataType x);

//尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);

//尾删
void SListPopBack(SLTNode** pphead);

//头删
void SListPopFront(SLTNode** pphead);

//查找
//可以充当修改
SLTNode* SlistFind(SLTNode* phead, SLTDataType x);

//在pos之前插入
void SlistInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);

//在pos之后插入
void SlistInsertAfter(SLTNode* pos, SLTDataType x);


//删除pos位置
void SlistErase(SLTNode** pphead, SLTNode* pos);

//删除pos后面位置
void SlistEraseAfter(SLTNode* pos, SLTDataType x);

Slist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Slist.h"

void SListPrint(SLTNode* phead)
{
	//不能断言,因为phead为空是正常情况,表示空链表
	SLTNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

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

	return newnode;
}

void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuySLTNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

void SListPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);

	SLTNode* newnode = BuySLTNode(x);

	//1. 链表为空
	//2. 链表非空

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		//找尾
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
		//尾插改变的是结构体成员,所以不用二级指针(结构体指针的指针),用结构体指针
	}
}


void SListPopBack(SLTNode** pphead)
{
	assert(pphead);
	//1.多个节点
	//2.一个节点
	if (*pphead == NULL)
	{
		return;
	}

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

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

	}

}



void SListPopFront(SLTNode** pphead)
{
	assert(pphead); 

	//温柔的检查
	if (*pphead == NULL)
	{
		return;
	}

	暴力检查
	//assert(*pphead != NULL);

	SLTNode* del = *pphead;
	*pphead = (*pphead)->next;

	free(del);
	del = NULL;
}

void SListDestory(SLTNode** pphead)
{
	assert(pphead);

	SLTNode* cur = *pphead;
	while (cur)
	{
		SLTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	*pphead = NULL;
}


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

void SlistInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;

			//暴力检查,pos不在链表中,prev为空还没有找到pos说明pos传错了
			assert(prev);
		}
		SLTNode* newnode = BuySLTNode(x);
		prev->next = newnode;
		newnode->next = pos;

	}

}

void SlistInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuySLTNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SlistErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SListPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
			//检查pos不是链表中节点,参数传错了
			assert(prev);
		}
		prev->next = pos->next;
		free(pos);
		
	}
}


void SlistInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	if (pos->next == NULL)
	{
		return;
	}
	else
	{
		SLTNode* next = pos->next;
		pos->next = next->next;
		free(next);
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Slist.h"

void TestSlist1()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPrint(plist);
}

void TestSlist2()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListDestory(&plist);

	SLTNode* pos = SlistFind(plist, 3);
	if (pos)
	{	
		//修改
		//SlistInsert(&plist, pos, 20);
		SlistErase(&plist, pos);
		printf("找到了\n");
	}
	else
	{
		printf("没找到\n");
	}


	SListPrint(plist);
}


int main()
{
	//TestSlist1();
	TestSlist2();

	return 0;
}

单链表只适合头插头删,时间复杂度 O(1)

(next);
}
}


`test.c`

```c
#define _CRT_SECURE_NO_WARNINGS 1
#include"Slist.h"

void TestSlist1()
{
	SLTNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);
	SListPrint(plist);
}

void TestSlist2()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListPopBack(&plist);
	//SListDestory(&plist);

	SLTNode* pos = SlistFind(plist, 3);
	if (pos)
	{	
		//修改
		//SlistInsert(&plist, pos, 20);
		SlistErase(&plist, pos);
		printf("找到了\n");
	}
	else
	{
		printf("没找到\n");
	}


	SListPrint(plist);
}


int main()
{
	//TestSlist1();
	TestSlist2();

	return 0;
}

单链表只适合头插头删,时间复杂度 O(1)

任意位置高效插入删除要交给之后文章讲解的双向链表。

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

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

相关文章

Jvm垃圾收集器系列之Parallel Scavenge收集器(个人见解仅供参考)

问&#xff1a;什么是Parallel Scavenge&#xff1f; 答&#xff1a;Parallel Scavenge是Java HotSpot虚拟机中的一种垃圾收集器&#xff0c;它主要用于提高应用程序的吞吐量。 问&#xff1a;Parallel Scavenge的主要目标是什么&#xff1f; 答&#xff1a;Parallel Scavenge的…

Hive - Select 使用 in 限制范围

目录 一.引言 二.Select Uid Info 1.少量 Uid 2.大量 Uid ◆ 建表 ◆ 本地 Load ◆ HDFS Load ◆ Select In 三.总结 一.引言 工业场景下 Hive 表通常使用 uid 作为用户维度构建和更新 Hive 表&#xff0c;当我们需要查询指定批次用户信息时&#xff0c;可以使用 in …

自制Java镜像发布到dockerhub公网使用

文章目录 问题现象解决制作Java镜像发布使用 问题现象 书接上回&#xff0c;上周处理了一个docker问题&#xff0c;写了篇博客&#xff1a;自定义docker镜像&#xff0c;ubuntu安装命令并导出我们使用谷歌的jib插件打包&#xff0c;详情可以参考这篇文章&#xff1a;Spring Bo…

unity C#中Array、Stack、Queue、Dictionary、HashSet优缺点和使用场景总结

文章目录 数组 (Array)列表 (List<T>)栈 (Stack<T>)队列 (Queue<T>)链表 (LinkedList<T>)哈希表 (Dictionary<TKey, TValue>) 或 HashSet<T>集合 (Collection<T>) 数组 (Array) 优点&#xff1a; 高效访问&#xff1a;通过索引可以…

uniCloud 云函数

相对于云函数&#xff0c;官方更推荐使用 云对象 新建云函数 编辑云函数 uniCloud-aliyun/cloudfunctions/hello_func/index.js use strict; exports.main async (event, context) > {let {name} eventreturn 你好&#xff0c;${name}! };云函数接收的参数从event中解构获…

二进制安装包安装Prometheus插件安装(mysql_exporter)

简介 mysql_exporter是用来收集MysQL或者Mariadb数据库相关指标的&#xff0c;mysql_exporter需要连接到数据库并有相关权限。既可以用二进制安装部署&#xff0c;也可以通过容器形式部署&#xff0c;但为了数据收集的准确性&#xff0c;推荐二进制安装。 一&#xff0c;下载安…

“百模大战:AI行业的技术竞争与发展趋势“

文章目录 每日一句正能量前言百模大战影响推理配置后记 每日一句正能量 修行不是抬起头朝天上看而是低下头学会谦卑和诚实。 前言 到目前为止&#xff0c;如果要评选2023年热度最高&#xff0c;影响力最大的一个概念&#xff0c;当属AI大模型了。这轮由ChatGPT引爆的技术热潮&…

K8S--部署SpringBoot项目实战

原文网址&#xff1a;K8S--部署SpringBoot项目实战-CSDN博客 简介 本文介绍K8S如何部署SpringBoot项目。 1.生成应用的docker镜像 把SpringBoot项目的jar包打包为docker镜像&#xff0c;见&#xff1a;Docker Compose--部署SpringBoot项目--实战-CSDN博客 创建后的镜像名称…

论文悦读(7)——NVM文件系统之Trio(SOSP‘23)文件系统

TRIO&#xff08;SOSP23&#xff09; 1. 背景&#xff08;Background&#xff09;1.1 NVM Technologis1.2 File System Customization1.3 Userspace NVM File Systems 2. 观察与动机&#xff08;Observation & Motivation&#xff09;3. 设计与实现&#xff08;Design &…

(18)Linux 实现简易版shell

前言&#xff1a;做一个 "会创建&#xff0c;会终止&#xff0c;会等待&#xff0c;会程序替换" 的简易 shell 。 1、显示提示符和获取用户输入 shell 本质就是个死循环&#xff0c;我们不关心获取这些属性的接口&#xff0c;如果要实现 shell&#xff1a; 1&…

conda环境下Could not create share link解决方法

1 问题描述 在运行chatglm-6B项目时&#xff0c;运行python web_demo.py&#xff0c;出现如下错误&#xff1a; (chatglm) [rootlocalhost ChatGLM2-6B]# python web_demo.py Loading checkpoint shards: 100%|██████████████████████████████…

scratch给数据清单排序 2023年12月中国电子学会图形化编程 少儿编程 scratch编程等级考试四级真题和答案解析

目录 scratch给数据清单排序 一、题目要求 1、准备工作 2、功能实现 二、案例分析

性能优化-OpenMP基础教程(三)-Android上运行OpenMP

本文主要介绍如何在一个常规的Android手机上调试OpenMP程序&#xff0c;包括Android NDK的环境配置和使用JNI编写一个OpenMP程序运行在Android手机中。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#…

js——json对象相互转化——js基础积累

js——json对象相互转化——js基础积累 需求场景解决步骤1&#xff1a;定义一个变量接收此字段&#xff0c;方便处理解决步骤2&#xff1a; { 外面的双引号要去掉解决步骤3&#xff1a;使用正则去除参数中的\\解决步骤4&#xff1a;如果此参数必须以{开头&#xff0c;以}结尾解…

Java学校教务管理系统源码带微信小程序

运行环境&#xff1a;jdk8mysql5.7IntelliJ IDEAmaven 技术&#xff1a;springbootmybatislayuishirojquery 教务管理系统是一个基于网络的在线管理平台, 帮助学校管理教务系统&#xff0c;用一个帐号解决学校教务教学管理&#xff0c; 灵活的定制符合学校自己实际情况的教务系…

Eclipse下安装GDB

主要参考资料&#xff1a; 链接: https://blog.csdn.net/u013609041/article/details/18967837 目录 简介Eclipse中安装和配置GDB错误 简介 Eclipse是一款开发软件。 GDB是一个调试软件&#xff0c;但是GDB通常是运行在linux下的&#xff0c;无法直接在windows下运行&#xff…

RabbitMQ安装与应用

文章目录 1. RabbitMQ1.1. 同步通讯与异步通讯1.2. 异步通讯的优缺点1.3. 几种MQ的对比1.4. docker安装运行RabbitMQ 流程1.5. RabbitMQ的几个概念1.6. 五种模型1.6.1. 基本消息队列 1.7. 基本使用1.7.1. 1建立连接时会出现以下界面![在这里插入图片描述](https://img-blog.csd…

Redis第四讲——Redis的数据库结构、删除策略及淘汰策略

一、redis中的数据库 redis服务器将所有数据库都保存在服务器状态redis.h/redisServer结构的db数组中。db数组的每项都是一个redis.h/redisDb结构&#xff0c;而每个redisDb结构就代表一个数据库。在初始化服务器时&#xff0c;程序会根据服务器状态的dbnum属性来决定应该创建多…

python+selenium爬虫笔记

本文只是做例子&#xff0c;具体网站路径麻烦你们换下&#xff0c;还有xpath路径也换下 一、安装所需要的组件&#xff08;此处采用谷歌&#xff09; 1、安装驱动 查看你的浏览器版本&#xff0c;去安装对应的版本 下载驱动 下载驱动路径 之前版本的 输入这个路径下载下来解压…

CTF-PWN-栈溢出-高级ROP-【SROP】

文章目录 linux信息处理2017 360春秋杯 smallest检查源码思路第一次要执行ret时的栈执行write函数时修改rsp到泄露的栈地址上去 输入/bin/sh并sigreturn调用系统调用回忆exp注意一个离离原上谱的地方 参考链接 SROP(Sigreturn Oriented Programming) 于 2014 年被 Vrije Univer…