C语言简单的数据结构:双向链表的实现

news2024/11/26 6:23:16

目录:

  • 1.双向链表的结构和初始化
    • 1.1双向链表的结构
    • 1.2双向链表的初始化
  • 2.双向链表的相关操作
    • 2.1双向链表的尾插、打印和头插
      • 2.11双向链表的尾插
      • 2.12双向链表的打印
      • 2.13双向链表的头插
    • 2.2双向链表的尾删和头删
      • 2.21双向链表的尾删
      • 2.22双向链表的头删
    • 2.3双向链表查找和任意位置插入、删除
      • 2.31双向链表查找
      • 2.32双向链表任意位置之后插入
      • 2.33双向链表任意位置删除
    • 2.4双向链表的销毁
  • 本文代码:

1.双向链表的结构和初始化

1.1双向链表的结构

之前我们的单向链表指的是:不带头单向不循环链表
而双向链表指的是:带头双向循环链表
在这里插入图片描述
其中头节点为哨兵位(不存储任何数据),尾部要指向哨兵位
它的节点有:数据,指向下一个节点的指针和指向下一个节点的指针
在这里插入图片描述
这就是双向链表的节点定义,写入VS:
在这里插入图片描述

1.2双向链表的初始化

当双向链表为NULL时,还有一个头节点,说一初始化时就是创建一个哨兵位
在这里插入图片描述

首先是申请链表:
在这里插入图片描述
在这里插入图片描述
接着是哨兵位的创建(放入一个无效数据就行)
在这里插入图片描述
测试:
在这里插入图片描述
在这里插入图片描述

2.双向链表的相关操作

2.1双向链表的尾插、打印和头插

2.11双向链表的尾插

这里的传参是什么呢?
在这里插入图片描述
58c66ffd3020b137a.png)
47be39ed4525a16999a787187d86.png)
对于这个问题其实很简单,对于完成操作来说这两种都可以,但是我们使用第一种是没办法改变哨兵位的,但是我们第二种有机会改变哨兵位的,如果让我们代码更安全一点,就使用第一个

在这里插入图片描述
在这里插入图片描述
这里的d3就是phead指向的prev指针
在这里插入图片描述
我们优先修改newnode指向,然后在修改phead的指向
我们再来判断NULL链表的情况:
当插入NULL指针时,并不会对NULL指针进行解引用,这个代码就没问题
在这里插入图片描述
这两行代码不能直接进行交换,因为会找不到d3这个数据,从而改变不了

2.12双向链表的打印

在这里插入图片描述
在这里插入图片描述
这里要用到哨兵为来确定是否循环一遍
测试:
在这里插入图片描述

2.13双向链表的头插

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
顺序为3 1 4 2
测试:
在这里插入图片描述

2.2双向链表的尾删和头删

在这里插入图片描述

2.21双向链表的尾删

首先要判断链表必须有效并且链表不能为NULL
在这里插入图片描述
在这里插入图片描述
我们只对2 3进行改变就行了
在这里插入图片描述
测试:
在这里插入图片描述

2.22双向链表的头删

在这里插入图片描述
在这里插入图片描述
测试:
在这里插入图片描述

2.3双向链表查找和任意位置插入、删除

2.31双向链表查找

在这里插入图片描述

进行遍历,然后进行判断
测试:
在这里插入图片描述

2.32双向链表任意位置之后插入

在这里插入图片描述
还是对newnode进行改变然后在该pos指针
测试:
在这里插入图片描述

2.33双向链表任意位置删除

删除位置理论上不能为phead,但是没有phead参数,无法增加校验
在这里插入图片描述
也是先将pos后的指针先修改好,然后在修改pos前的指针
测试:
在这里插入图片描述
这里虽然对实参进行改变了,但这里为什么不传二级指针呢
这里是为了保持接口一致性,方便我们的记忆,我们可以通过外部值NULL来解决
在这里插入图片描述
这里我们之前的初始化代码也可以进行优化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
一个以参数的方式初始化,一个以返回值的方法初始化

2.4双向链表的销毁

销毁后要向下走,所以我们要将指针存一下
在这里插入图片描述
这里也是一级指针,所以无法改变头节点
在这里插入图片描述
手动置为NULL即可

本文代码:

List.h

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

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;

LTNode* LTBuyNode(LTDataType x);//申请空间
//void LTInit(LTNode** pphead);//初始化
LTNode* LTInit();

void LTPrint(LTNode* phead);
//插入数据
void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);

void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);

LTNode* LTFind(LTNode* phead, LTDataType x);
void LTInsert(LTNode* pos, LTDataType x);
void LTErase(LTNode* pos);

void LTDestroy(LTNode* phead);

List.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"

LTNode* LTBuyNode(LTDataType x)
{
	//申请节点
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc");
		exit(1);
	}
	node->data = x;
	node->next = node->prev = node;
	return node;
}

//void LTInit(LTNode** pphead)
//{
//	*pphead = LTBuyNode(-1);
//}

LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}


void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev newnode
	newnode->prev = phead->prev;
	newnode->next = phead;

	phead->prev->next = newnode;
	phead->prev = newnode;
}

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);

	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

void LTPopBack(LTNode* phead)
{
	//链表必须有效并且链表不能为NULL
	assert(phead && phead->next != phead);

	LTNode* del = phead->prev;
	del->prev->next = phead;
	phead->prev = del->prev;
	free(del);
	del = NULL;
}

void LTPopFront(LTNode* phead)
{
	assert(phead && phead->next != phead);
	
	LTNode* del = phead->next;
	phead->next = del->next;
	del->next->prev = phead;
	free(del);
	del = NULL;
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = LTBuyNode(x);
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}

void LTErase(LTNode* pos)
{
	assert(pos);
	//pos理论上不能为phead,但是没有phead参数,无法增加校验

	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
	pos = NULL;
}

void LTDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"

void ListTest01()
{
	//LTNode* plist = NULL;
	//LTInit(&plist);
	LTNode* plist = LTInit();

	//LTPushBack(plist, 1);
	//LTPrint(plist);
	//LTPushBack(plist, 2);
	//LTPrint(plist);
	//LTPushBack(plist, 3);
	//LTPrint(plist);
	//LTPushBack(plist, 4);
	//LTPrint(plist);
	LTPushFront(plist, 4);
	LTPrint(plist);
	LTPushFront(plist, 3);
	LTPrint(plist);
	LTPushFront(plist, 2);
	LTPrint(plist);
	LTPushFront(plist, 1);
	LTPrint(plist);
	LTDestroy(plist);
	plist = NULL;
}

void ListTest02()
{
	//LTNode* plist = NULL;
	//LTInit(&plist);
	LTNode* plist = LTInit();

	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPrint(plist);
	//LTPopBack(plist);
	//LTPrint(plist);
	//LTPopBack(plist);
	//LTPrint(plist);
	//LTPopBack(plist);
	//LTPrint(plist);
	//LTPopBack(plist);
	//LTPrint(plist);
	LTPopFront(plist);
	LTPrint(plist);
	LTPopFront(plist);
	LTPrint(plist);
	LTPopFront(plist);
	LTPrint(plist);
	LTPopFront(plist);
	LTPrint(plist);
	LTDestroy(plist);
	plist = NULL;
}

void ListTest03()
{
	//LTNode* plist = NULL;
	//LTInit(&plist);
	LTNode* plist = LTInit();

	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPrint(plist);
	LTNode* find1 = LTFind(plist, 3);
	if (find1 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了\n");
	}
	LTNode* find2 = LTFind(plist, 5);
	if (find2 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了\n");
	}
	LTDestroy(plist);
	plist = NULL;
}

void ListTest04()
{
	//LTNode* plist = NULL;
	//LTInit(&plist);
	LTNode* plist = LTInit();

	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPrint(plist);
	LTNode* find1 = LTFind(plist, 2);
	LTInsert(find1, 66);
	LTPrint(plist);
	LTNode* find2 = LTFind(plist, 4);
	LTInsert(find2, 99);
	LTPrint(plist);
	LTNode* find3 = LTFind(plist, 66);
	LTErase(find3);
	find3 = NULL;
	LTPrint(plist);
	LTNode* find4 = LTFind(plist, 99);
	LTErase(find4);
	find4 = NULL;
	LTPrint(plist);
	LTDestroy(plist);
	plist = NULL;
}

int main()
{
	//ListTest01();
	//ListTest02();
	//ListTest03();
	ListTest04();
	return 0;
}

以上就是本文的全部内容了,希望对大家有所帮助,大家加油!!!

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

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

相关文章

Linux 网络测速

1.开发背景 网络测速&#xff0c;为了测试开发板的网络速度是否达标的通用测试方法 2.开发需求 搭建 iperf3 &#xff0c;在 ubuntu 下安装服务端&#xff0c;在板卡上安装客户端&#xff0c;服务端和客户端互发 3.开发环境 ubuntu20.04 嵌入式开发板&#xff08;debian 千…

了解MySQL InnoDB多版本MVCC(Multi-Version Concurrency Control)

了解MySQL InnoDB多版本MVCC&#xff08;Multi-Version Concurrency Control&#xff09; 在数据库管理系统中&#xff0c;多版本并发控制&#xff08;MVCC&#xff09;是一种用于实现高并发和事务隔离的技术。MySQL的InnoDB存储引擎支持MVCC&#xff0c;这使得它可以在提供高…

22长安杯电子取证复现(检材一,二)

检材一 先用VC容器挂载&#xff0c;拿到完整的检材 从检材一入手&#xff0c;火眼创建案件&#xff0c;打开检材一 1.检材1的SHA256值为 计算SHA256值&#xff0c;直接用火眼计算哈希计算 9E48BB2CAE5C1D93BAF572E3646D2ECD26080B70413DC7DC4131F88289F49E34 2.分析检材1&am…

Spring (三) 之Aop及事务控制

文章目录 目标 一、AOP 思想和重要术语&#xff08;理解&#xff09;1、需求问题2、AOP3、AOP 术语 二、AOP 实现及 Pointcut 表达式&#xff08;了解&#xff09;1、AOP 规范及实现2、AspectJ3、AspectJ 切入点语法&#xff08;掌握&#xff09;3.1、切入点语法通配符3.2、切入…

Linux 网络基本命令

一、查看网络信息 ifconfig 二、关闭网络 ifdown ens33 (有的电脑不一定是ens33&#xff0c;具体看上图画线的地方) 三、开启网络 ifup ens33

【电路笔记】-数字逻辑门总结

数字逻辑门总结 文章目录 数字逻辑门总结1、概述2、逻辑门真值表3、总结 数字逻辑门有三种基本类型&#xff1a;与门、或门和非门。 1、概述 我们还看到&#xff0c;数字逻辑门具有与其相反或互补的形式&#xff0c;分别为“与非门”、“或非门”和“缓冲器”&#xff0c;并且…

RK3588 Android13 鼠标风格自定义动态切换

前言 电视产品,客户提供了三套鼠标图标过来,要求替换系统中原有丑陋风格且要支持动态切换, 并且在 TvSetting 中要有菜单,客户说啥就是啥呗,开整。 效果图 test framework 部分修改文件清单 png 为鼠标风格资源图片,这里就不提供了,可自由找一个替换一下就行 framew…

C语言野指针【入门详解】

目录 一、什么是野指针 二、野指针的成因 2.1 指针未初始化 2.2 指针越界访问 2.3 指针指向的空间释放 三、如何规避野指针 3.1 初始化指针 3.2 小心越界访问 3.3 当指针不用时&#xff0c;及时置为空 3.4 避免返回局部变量的地址 *结语&#xff1a; 希望这篇关于指…

[SWPUCTF 2021 新生赛]jicao、easy_md5

目录 一、[SWPUCTF 2021 新生赛]jicao 什么是JSON&#xff1f; JSON语法&#xff1a; [SWPUCTF 2021 新生赛]jicao 二、[SWPUCTF 2021 新生赛]easy_md5 PHP弱类型和强类型 1.弱类型比较&#xff08;&#xff09; 2.强类型比较&#xff08;&#xff09; [SWPUCTF 2021 …

OceanBase数据库日常运维快速上手

这里为大家汇总了从租户创建、连接数据库&#xff0c;到数据库的备份、归档、资源配置调整等&#xff0c;在OceanBase数据库日常运维中的操作指南。 创建租户 方法一&#xff1a;通过OCP 创建 确认可分配资源 想要了解具体可分配的内存量&#xff0c;可以通过【资源管理】功…

华为OD机试 - 结队编程(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

Google DeepMind: Many-Shot vs. Few-Shot

本文介绍了如何通过增大上下文窗口&#xff0c;利用大型语言模型&#xff08;LLMs&#xff09;进行多实例上下文学习&#xff08;Many-Shot In-Context Learning&#xff0c;ICL&#xff09;的方法。主要描述了现有的几实例上下文学习方法虽然在推理时能够通过少量例子学习&…

MicroSIP电话呼叫软件使用及配置方法

MicroSIP是一款开源的SIP协议电话软件&#xff0c;它可以帮助你在计算机上进行语音和视频通话。下面是关于如何使用和配置MicroSIP的一些基本步骤&#xff1a; 安装MicroSIP 从MicroSIP官方网站下载适合你操作系统的安装包23。 解压下载的文件&#xff0c;并运行安装程序。 …

可以与 FastAPI 不分伯仲的 Python 著名的 Web 框架

正如你所理解的&#xff0c;任何领域都不可能停止进步&#xff0c;不断使用相同的工具意味着不思进取。这一点在信息技术领域&#xff0c;尤其是网络开发行业非常明显。 关于网络框架&#xff0c;不论是 Django 和 Flask 等传统框架还是 Python 的新型高级框架&#xff0c;一直…

算法课程笔记——常用库函数

memset初始化 设置成0是可以每个设置为0 而1时会特别大 -1的补码是11111111 要先排序 unique得到的是地址 地址减去得到下标 结果会放到后面 如果这样非相邻 会出错 要先用sort排序 O&#xff08;n&#xff09;被O&#xff08;nlogn&#xff09;覆盖

【智能算法】饥饿游戏搜索算法(HGS)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2021年&#xff0c;Yang等人受到自然界饥饿驱动的活动和动物的行为选择启发&#xff0c;提出了饥饿游戏搜索算法&#xff08;Hunger Games Search, HGS&#xff09;。 2.算法原理 2.1算法思想 HGS…

【Java网络编程】网络编程概述、UDP通信(DatagramPacket 与 DatagramSocket)

目录 1、网络编程 1.1、概述 1.1、网络编程三要素 2、UDP通信 2.1、DatagramPacket 与 DatagramSocket 2.1、 UDP发收数据示例 1、网络编程 1.1、概述 在网络通信协议下&#xff0c;不同计算机上运行的程序&#xff0c;进行的数据传输应用场景&#xff1a;即时通信、网游…

韩顺平Java | C27 正则表达式

入门介绍 需求&#xff1a;提取文本中某类字符 传统方法&#xff1a;遍历每个字符&#xff0c;判断其是否在ASCII码中某种类型得编码范围内&#xff0c;代码量大&#xff0c;效率不高 正则表达式(RegExp, regular expression)&#xff1a;处理文本的利器&#xff0c;是对字符…

网络编程套接字(三)之TCP服务器简单实现

目录 一、服务端TcpServer 1、tcp_server.hpp 2、tcp_server.cc 二、客户端TcpClient tcp_client.cc 三、服务器和客户端进行通信 四、完整代码 一、服务端TcpServer 首先我们需要对服务端进行封装。我们需要的成员变量有IP地址&#xff0c;端口号port&#xff0c;以及监…

49.基于SpringBoot + Vue实现的前后端分离-爱心公益网站系统(项目 + 论文)

项目介绍 本站是一个B/S模式系统&#xff0c;采用SpringBoot Vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得基于SpringBoot Vue技术的爱心公益网站系统设计与实现管理工作…