C语言:数据结构(双向链表)

目录

  • 1、双向链表的结构
  • 2、顺序表和双向链表的优缺点分析
  • 3、双向链表的实现

1、双向链表的结构

在这里插入图片描述

注意:这⾥的“带头“跟前面我们说的“头节点”是两个概念,实际前面的在单链表阶段称呼不严谨,但是为了更好的理解就直接称为单链表的头节点。
带头链表里的头节点,实际为“放哨的”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”。
“哨兵位”存在的意义:遍历循环链表避免死循环。

2、顺序表和双向链表的优缺点分析

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持O(N)
任意位置插⼊或者删除元素可能需要搬移元素,效率低只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储和频繁访问任意位置频繁插入和删除

3、双向链表的实现

ListNode.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//定义双向链表节点的结构
typedef int Ltdatatype;
typedef struct ListNode
{
	Ltdatatype data;
	struct ListNode* prev;//指向前一个节点的指针
	struct ListNode* next;//指向后一个节点的指针
}ListNode;
//双向链表的初始化
ListNode* LtInit();
//尾插
//不改变哨兵位的地址,所以传一级即可
void LtPushBack(ListNode* phead, Ltdatatype x);//插入数据之前,链表必须初始化到只有一个头结点的情况
//打印链表
void LtPrint(ListNode* phead);
//头插
void LtPushFront(ListNode* phead, Ltdatatype x);
//尾删
LtPopBack(ListNode* phead);
//头删
LtPopFront(ListNode* phead);
//查找
ListNode* LtFind(ListNode* phead, Ltdatatype x);
//指定位置前插入
void LtInsert(ListNode* pos, Ltdatatype x);
//删除pos位置
void LtErase(ListNode* pos);
//销毁链表
void LtDestroy(ListNode* phead);

ListNode.c

#define _CRT_SECURE_NO_WARNINGS
#include "ListNode.h"
//申请节点
ListNode* LtBuyNode(Ltdatatype x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	//申请成功
	node->data = x;
	node->next = node->prev = node;
	return node;
}
//双向链表的初始化
ListNode* LtInit()
{
	ListNode*phead = LtBuyNode(-1);
	return phead;
}
//尾插
void LtPushBack(ListNode* phead, Ltdatatype x)
{
	assert(phead);
	ListNode* newnode = LtBuyNode(x);
	//改变新节点的指向
	newnode->prev = phead->prev;
	newnode->next = phead;
	//改变尾节点和哨兵位的指向
	phead->prev->next = newnode;
	phead->prev = newnode;
}
//打印链表
void LtPrint(ListNode* phead)
{
	ListNode* pcur = phead->next;
	//遍历链表
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}
//头插
void LtPushFront(ListNode* phead,Ltdatatype x)
{
	assert(phead);
	ListNode* newnode = LtBuyNode(x);
	newnode->prev = phead;
	newnode->next = phead->next;
	//修改哨兵位和第一个有效节点的指向
	phead->next->prev = newnode;
	phead->next = newnode;
}
//尾删
LtPopBack(ListNode* phead)
{
	//链表必须有效且链表不能为空(只有一个哨兵位)
	assert(phead && phead->next != phead);
	ListNode* Del = phead->prev;//尾节点
	ListNode* DelPrev = Del->prev;//尾节点的前一个节点
	phead->prev = DelPrev;
	DelPrev->next = phead;
	free(Del);//删除Del节点
	Del = NULL;
}
//头删
LtPopFront(ListNode* phead)
{
	//判断链表是否有效和链表是否为空
	assert(phead && phead->next != phead);
	ListNode* Del = phead->next;//第一个有效节点
	ListNode* DelNext = Del->next;//有效节点的下一个节点
	phead->next = DelNext;
	DelNext->prev = phead;
	free(Del);//删除Del节点
	Del = NULL;
}
//查找
ListNode* LtFind(ListNode* phead, Ltdatatype x)
{
	ListNode* pcur = phead->next;
	//遍历链表
	while (pcur != phead)
	{
		if (pcur->data == x)
			return pcur;
		pcur = pcur->next;//继续让pcur往下遍历
	}
	return NULL;//没有找到
}
//在pos位置之前插入数据
void LtInsert(ListNode* pos,Ltdatatype x)
{
	ListNode* newnode = LtBuyNode(x);
	newnode->prev = pos->prev;
	newnode->next = pos;
	pos->prev->next = newnode;
	pos->prev = newnode;
}
//删除pos位置
void LtErase(ListNode* pos)
{
	assert(pos);
	ListNode* PosPrev = pos->prev;//pos的前一个节点
	ListNode* PosNext = pos->next;//pos的后一个节点
	PosPrev->next = PosNext;
	PosNext->prev = PosPrev;
	free(pos);
	//pos = NULL;这里就算置空了,也不会影响实参
}
//销毁链表
void LtDestroy(ListNode* phead)
{
	ListNode* pcur = phead->next;
	//边遍历边释放节点
	while (pcur != phead)
	{
		ListNode* Next = pcur->next;//保存要释放掉节点的下一个地址
		free(pcur);
		pcur = Next;
	}
	//此时pcur指向phead,而phead还没有被销毁
	free(phead);
	pcur = NULL;
}

text.c

#define _CRT_SECURE_NO_WARNINGS
#include "ListNode.h"
void LtnodeTest()
{
	//测试初始化
	ListNode* plist = LtInit();
	//测试尾插
	LtPushBack(plist,1);
	LtPushBack(plist,2);
	LtPushBack(plist,3);
	//测试打印
	LtPrint(plist);
	//测试头插
	//LtPushFront(plist,4);
	//LtPushFront(plist,5);
	//LtPushFront(plist,6);
	//LtPrint(plist);
	//测试尾删
	LtPopBack(plist);
	LtPrint(plist);
	//测试头删
	//LtPopFront(plist);
	//LtPrint(plist);
	//测试查找
	//ListNode*find = LtFind(plist,2);
	//if (find)
	//	printf("找到了!\n");
	//else
	//	printf("没找到!\n");
	//测试在pos位置之前插入数据
	//LtInsert(find,88);
	//LtPrint(plist);
	//测试删除pos位置
	//LtErase(find);
	//find = NULL;//形参的改变不会影响实参,所以要在函数调用结束之后置为空
	//LtPrint(plist);
	//测试销毁链表
	//LtDestroy(plist);
	//plist = NULL;
}
int main()
{
	LtnodeTest();
	return 0;
}

如果对你有所帮助的话,别忘了一键三连哟,谢谢宝子们😘!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

QT之信号和槽

在刚刚了解Qt的时候&#xff0c;为了通过按钮显示 hello world 的时候曾说明过信号与槽&#xff0c;虽然没有细说&#xff0c;不过也算是接触过。 而本文就会细细说明什么是 Qt 的信号与槽。 概念初识 在 linux 学进程相关的内容的时候&#xff0c;曾了解过信号是操作系统控…

【STM32】快速使用F407通用定时器输出可变PWM

网上的文章太啰嗦&#xff0c;这里直接开始。 使用的是STM32CubeIDE&#xff0c;HAL。以通用定时器TIM12在 通道2上输出1KHz的PWM为例。 要确定输出的引脚、定时器连接在哪里。 TIM2、3、4、5、12、13、14在APB1上&#xff0c;最大计数频率84M。 TIM1、8、9、10、11在APB2…

Python 与 TensorFlow2 生成式 AI(二)

原文&#xff1a;zh.annas-archive.org/md5/d06d282ea0d9c23c57f0ce31225acf76 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第四章&#xff1a;教授网络生成数字 在前一章中&#xff0c;我们涵盖了神经网络模型的构建基块。在这一章中&#xff0c;我们的第一个项目…

HIVE启动步骤

不如意的时候不要尽往悲伤里钻 想想有笑声的日子 启动HIEV 1.启动虚拟机Hadoop集群 2.连接Linux 3.start-all.sh 4.hive 5.hive启动时报错 当我们启动Hadoop集群时 启动hive可能会出现卡在true处不动的情况 那么我们只需要做一个操作就可以解决问题啦 hdfs haadmin -transitio…

ASP.NET数据存储与交换系统设计

摘 要 该系统以Microsoft Visual Studio 2003作为开发工具&#xff0c;选用SQL Server 2000数据库来实现数据存储&#xff0c;并设计开发了一种基于B/S模式的数据存储与交换系统。该系统完成了用户注册管理、后台管理和用户空间管理功能&#xff1b;为每个用户提供了个人的存…

数据库(MySQL)—— DQL语句(基本查询和条件查询)

数据库&#xff08;MySQL&#xff09;—— DQL语句&#xff08;基本查询和条件查询&#xff09; 什么是DQL语句基本查询查询多个字段字段设置别名去除重复记录 条件查询语法条件 我们今天进入MySQL的DQL语句的学习&#xff1a; 什么是DQL语句 MySQL中的DQL&#xff08;Data Q…

【ARM 裸机】NXP 官方 SDK 使用

在前几节中&#xff0c;学习了如何编写汇编的 led 驱动、C 语言的 led 驱动、模仿 STM32 进行开发&#xff0c;我们都是自己写外设寄存器的结构体&#xff0c;外设非常的多&#xff0c;写起来费时费力&#xff0c;NXP 针对 I.MX6ULL 编写了一个 SDK 包&#xff0c;这个 SDK 包就…

python的输入输出(爽文,备忘,查询,友好)

Python中的输入输出主要涉及到输入函数和输出函数。 输出函数&#xff1a;print() print() 函数用于将信息输出到屏幕上。它可以输出字符串、变量的值&#xff0c;以及其他各种数据类型。 name "Alice" age 30 print("姓名:", name, "年龄:&quo…

OpenCV如何实现背投(58)

返回:OpenCV系列文章目录&#xff08;持续更新中......&#xff09; 上一篇&#xff1a;OpenCV直方图比较(57) 下一篇&#xff1a;OpenCV如何模板匹配(59) 目标 在本教程中&#xff0c;您将学习&#xff1a; 什么是背投以及它为什么有用如何使用 OpenCV 函数 cv::calcBackP…

轻松下载小程序短剧视频,你不可错过的神器!

探索小程序的精彩短剧&#xff0c;一款神器让您的收藏变得触手可及。轻松下载&#xff0c;无损享受&#xff0c;每个精彩瞬间&#xff0c;都不让你错过。让这款下载神器成为你掌中的宝藏&#xff0c;开启随时随地的精彩观影体验。 这个神器就是下载高手 下载小程序视频的工具…

GoLang Gin实际使用

所有代码同步到Admin/gitDemo - Gitee.comhttps://gitee.com/mec-deployment-team_0/git-demo/tree/dev/ 1.创建Gin框架 一般设计一个常规的web项目&#xff0c;都需要以下几个模块 runApp 主函数&#xff0c;运行整个项目routes 路由控制&#xff0c;管理跳转以及路由分组co…

CTF-Show nodejs

web334 下载附件&#xff0c;有两个文件 在Character.toUpperCase()函数中&#xff0c;字符ı会转变为I&#xff0c;字符ſ会变为S。 在Character.toLowerCase()函数中&#xff0c;字符İ会转变为i&#xff0c;字符K会转变为k。 所以用ctfſhow 123456登录就可以出flag了 w…

力扣刷题第一天:消失的数字

大家好啊&#xff0c;从今天开始将会和大家一起刷题&#xff0c;从今天开始小生也会开辟新的专栏。&#x1f61c;&#x1f61c;&#x1f61c; 目录 第一部分&#xff1a;题目描述 第二部分&#xff1a;题目分析 第三部分&#xff1a;解决方法 3.1 思路一&#xff1a;先排序…

LeetCode1005:K次取反后最大化的数组和

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数组 可能的最大和 。 解题思想 第…

AC自动机

AC自动机 AC自动机有一个很出色的功能&#xff1a;实现多模式匹配。 多模式匹配&#xff1a;模式串有多个&#xff0c;主串只有一个&#xff0c;要进行多次模式串匹配。如果用KMP就要一个一个模式串进行匹配&#xff0c;效率低。AC自动机就可以做到&#xff0c;只要经过一些预…

深度学习中权重初始化的重要性

深度学习模型中的权重初始化经常被人忽略&#xff0c;而事实上这是非常重要的一个步骤&#xff0c;模型的初始化权重的好坏关系到模型的训练成功与否&#xff0c;以及训练速度是否快速&#xff0c;效果是否更好等等&#xff0c;这次我们专门来看看深度学习中的权重初始化问题。…

RuoYi-Vue-Plus (SPEL 表达式)

RuoYi-Vue-Plus 中SPEL使用 DataScopeType 枚举类中&#xff1a; /*** 部门数据权限*/DEPT("3", " #{#deptName} #{#user.deptId} ", " 1 0 "), PlusDataPermissionHandler 拦截器中定义了解析器&#xff1a; buildDataFilter 方法中根据注解的…

基于电磁激励原理利用视触觉传感器估计抓取力矩的方法

由于触觉感知能使机器人通过其触觉传递获取丰富的接触信息&#xff0c;触觉感知已经成为机器人机械臂的一种流行的感知方式。而在触觉传感器可获取的各种信息中&#xff0c;通过外界接触从抓取物体传递到机器人手指的力矩等信息&#xff0c;在完成各种指令的实现尤为重要。如图…

数据结构––队列

1.队列的定义 2.队列的分类 2.1循环队 2.2链式队 3.队列的实现 3.1循环队 3.1.1声明 typedef int QDataType; #define MAXSIZE 50 //定义元素的最大个数 /*循环队列的顺序存储结构*/ typedef struct {QDataType *data;int front; //头指针int rear; //尾指针 }Queue;…

【最大公约数 排序】2344. 使数组可以被整除的最少删除次数

本文涉及知识点 最大公约数 排序 LeetCode2344. 使数组可以被整除的最少删除次数 给你两个正整数数组 nums 和 numsDivide 。你可以从 nums 中删除任意数目的元素。 请你返回使 nums 中 最小 元素可以整除 numsDivide 中所有元素的 最少 删除次数。如果无法得到这样的元素&a…
最新文章