双向链表专题

news2024/10/7 2:24:48

文章目录

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

目录

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

1. 双向链表的结构

带头双向循环链表
注意:

这⾥的“带头”跟前面我们说的“头节点”是两个概念,带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”。

“哨兵位”存在的意义:遍历循环链表避免死循环。

2. 双向链表的实现

  1. 定义双向链表中节点的结构
//定义双向链表中节点的结构
typedef int LTDataType;

typedef struct ListNode
{
	LTDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;
  1. 初始化

注意,双向链表是带有哨兵位的,插入数据之前链表中必须要先初始化一个哨兵位

void LTInit(LTNode** pphead)
{
	*pphead = (LTNode*)malloc(sizeof(LTNode));

	if (NULL == *pphead)
	{
		perror("malloc fail!");
		exit(1);
	}

	(*pphead)->data = -1;
	(*pphead)->next = (*pphead)->prev = *pphead;
}

也可以这样写:

LTNode* LTInit()
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));

	if (NULL == phead)
	{
		perror("malloc fail!");
		exit(1);
	}

	phead->data = -1;
	phead->next = phead->prev = phead;

	return phead;
}
  1. 尾插

概念: 当链表中只有哨兵位节点的时候,我们称该链表为空链表;即哨兵位是不能删除的。

不需要改变哨兵位,则不需要传二级指针;如果需要修改哨兵位的话,则传二级指针。

尾插

LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));

	if (NULL == newnode)
	{
		perror("malloc fail!");
		exit(1);
	}

	newnode->data = x;
	newnode->next = newnode->prev = newnode;

	return newnode;
}

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev(ptail) newnode
	newnode->next = phead;
	newnode->prev = phead->prev;
	phead->prev->next = newnode;
	phead->prev = newnode;
}

因为写了申请新节点的函数,所以上面的初始化代码可以优化:

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

	return phead;
}
  1. 打印
void LTPrint(LTNode* phead)
{
	//phead不能为空
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}

	printf("\n");
}
  1. 头插

头插

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;
	phead->next->prev = newnode;
	phead->next = newnode;
}
  1. 尾删

尾删

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	//链表为空:只有一个哨兵位节点
	assert(phead->next != phead);//若哨兵位节点的next指针或者prev指针指向的是自己,说明当前链表为空

	LTNode* del = phead->prev;
	LTNode* prev = del->prev;

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

	free(del);
	del = NULL;
}
  1. 头删

头删

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	LTNode* del = phead->next;
	LTNode* next = del->next;

	//phead del next
	next->prev = phead;
	phead->next = next;

	free(del);
	del = NULL;
}
  1. 查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (x == pcur->data)
		{
			return pcur;
		}

		pcur = pcur->next;
	}

	return NULL;
}
  1. 在pos位置之后插入数据
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

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

	pos->next->prev = newnode;
	pos->next = newnode;
}
  1. 删除pos位置的数据

删除pos位置的数据

//删除pos位置的数据
void LTErase(LTNode* pos)
{
	assert(pos);

	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}
  1. 销毁
void LTDesTroy(LTNode** pphead)
{
	assert(pphead);
	//哨兵位不能为空
	assert(*pphead);

	LTNode* pcur = (*pphead)->next;

	while (pcur != *pphead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}

	//链表中只有一个哨兵位
	free(*pphead);
	*pphead = 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;
}

但是要注意:这样写要在调用完函数后再写一句 plist = NULL;

这两种写法我们更推荐第二种:推荐传一级指针**(保持接口一致性)**

完整代码:

//List.h

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

//定义双向链表中节点的结构
typedef int LTDataType;

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

//注意,双向链表是带有哨兵位的,插入数据之前链表中必须要先初始化一个哨兵位
//void LTInit(LTNode** pphead);
LTNode* LTInit();
//void LTDesTroy(LTNode** pphead);
void LTDesTroy(LTNode* phead);//推荐传一级指针(保持接口一致性)

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);

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置的数据
void LTErase(LTNode* pos);
//List.c

#include "List.h"

//void LTInit(LTNode** pphead)
//{
//	*pphead = (LTNode*)malloc(sizeof(LTNode));
//
//	if (NULL == *pphead)
//	{
//		perror("malloc fail!");
//		exit(1);
//	}
//
//	(*pphead)->data = -1;
//	(*pphead)->next = (*pphead)->prev = *pphead;
//}

LTNode* LTBuyNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));

	if (NULL == newnode)
	{
		perror("malloc fail!");
		exit(1);
	}

	newnode->data = x;
	newnode->next = newnode->prev = newnode;

	return newnode;
}

//LTNode* LTInit()
//{
//	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
//
//	if (NULL == phead)
//	{
//		perror("malloc fail!");
//		exit(1);
//	}
//
//	phead->data = -1;
//	phead->next = phead->prev = phead;
//
//	return phead;
//}

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

	return phead;
}

//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev(ptail) newnode
	newnode->next = phead;
	newnode->prev = phead->prev;
	phead->prev->next = newnode;
	phead->prev = newnode;
}

//头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;
	phead->next->prev = newnode;
	phead->next = newnode;
}

void LTPrint(LTNode* phead)
{
	//phead不能为空
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}

	printf("\n");
}

//尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	//链表为空:只有一个哨兵位节点
	assert(phead->next != phead);//若哨兵位节点的next指针或者prev指针指向的是自己,说明当前链表为空

	LTNode* del = phead->prev;
	LTNode* prev = del->prev;

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

	free(del);
	del = NULL;
}

//头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);

	LTNode* del = phead->next;
	LTNode* next = del->next;

	//phead del next
	next->prev = phead;
	phead->next = next;

	free(del);
	del = NULL;
}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* pcur = phead->next;

	while (pcur != phead)
	{
		if (x == pcur->data)
		{
			return pcur;
		}

		pcur = pcur->next;
	}

	return NULL;
}

//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

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

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

//删除pos位置的数据
void LTErase(LTNode* pos)
{
	assert(pos);

	//pos->prev pos pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

//void LTDesTroy(LTNode** pphead)
//{
//	assert(pphead);
//	//哨兵位不能为空
//	assert(*pphead);
//
//	LTNode* pcur = (*pphead)->next;
//
//	while (pcur != *pphead)
//	{
//		LTNode* next = pcur->next;
//		free(pcur);
//		pcur = next;
//	}
//
//	//链表中只有一个哨兵位
//	free(*pphead);
//	*pphead = 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

#include "List.h"

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

	LTNode* plist = LTInit();
	//尾插
	//LTPushBack(plist, 1);
	//LTPushBack(plist, 2);
	//LTPushBack(plist, 3);
	//LTPushBack(plist, 4);
	//LTPrint(plist);

	//头插
	LTPushFront(plist, 1);
	LTPushFront(plist, 2);
	LTPushFront(plist, 3);
	LTPushFront(plist, 4);
	LTPrint(plist);

	//LTPopBack(plist);
	//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);
	LTPopFront(plist);
	LTPrint(plist);

	LTNode* findRet = LTFind(plist, 3);

	//if (NULL == findRet)
	//{
	//	printf("未找到!\n");
	//}
	//else
	//{
	//	printf("找到了!\n");
	//}

	//在指定位置之后插入数据
	//LTInsert(findRet, 66);
	//LTPrint(plist);

	//删除pos位置的节点
	LTErase(findRet);
	LTPrint(plist);

	//LTDesTroy(&plist);
	LTDesTroy(plist);
	plist = NULL;
}

int main()
{
	ListTest01();

	return 0;
}

3. 顺序表和双向链表的优缺点分析

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

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

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

相关文章

强大CSS3可视化代码生成器

DIY可视化CSS可视化代码生成器是一种工具&#xff0c;它允许用户通过图形界面直观地创建和编辑CSS样式&#xff0c;然后自动生成相应的CSS代码。DIY可视化对于那些不熟悉CSS语法或者想要更直观地调整样式的人来说特别有用。 提供了强大的CSS可视化编辑功能&#xff0c;用户可以…

【牛客网】:链表的回文结构(提升)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;每日一练 &#x1f337;追光的人&#xff0c;终会万丈光芒 目录 &#x1f3dd;问题描述&#xff1a; &#x1f3dd;问题分析&#xff1a; 步骤一&#xff1a;查找链表的中间节点 步骤二&am…

小红书美妆类笔记文案有哪些特点?

小红书作为一个分享和发现美好生活的平台&#xff0c;美妆护肤时尚类笔记在其中占据了重要的地位。这些笔记的文案特点通常表现为以下几点&#xff1a; 1.**情感化叙述**&#xff1a; 这类文案往往运用生动形象的词汇和细腻的情感表达&#xff0c;使文案更贴近读者的内心感受…

vue使用海康控件开发包——浏览器直接查看海康监控画面

1、下载控件开发包 2、安装插件&#xff08;双击/demo/codebase/HCWebSDKPlugin.exe进行安装&#xff09; 3、打开/demo/index.html文件 4、在页面上输入你的海康监控的登录信息进行预览 如果有监控画面则可以进行下面的操作 注意&#xff1a;以下操作都在Vue项目进行 5、复…

【笔试强训】除2!

登录—专业IT笔试面试备考平台_牛客网牛客网是互联网求职神器&#xff0c;C、Java、前端、产品、运营技能学习/备考/求职题库&#xff0c;在线进行百度阿里腾讯网易等互联网名企笔试面试模拟考试练习,和牛人一起讨论经典试题,全面提升你的技术能力https://ac.nowcoder.com/acm/…

MySQL面试——聚簇/非聚簇索引

存储引擎是针对表结构&#xff0c;不是数据库 引擎层&#xff1a;对数据层以何种方式进行组织 update&#xff1a;加索引&#xff1a;行级锁&#xff1b;不加索引&#xff1a;表级锁

BGP的基本概念和工作原理

AS的由来 l Autonomous System 自治系统&#xff0c;为了便于管理规模不断扩大的网络&#xff0c;将网络划分为不同的AS l 不同AS通过AS号区分&#xff0c;AS号取值范围1&#xff0d;65535&#xff0c;其中64512&#xff0d;65535是私有AS号 l IANA机构负责AS号的分发 AS之…

计算机体系结构与OS管理

冯诺依曼体系结构 我们只看数字信号&#xff08;红色的线&#xff09;&#xff1a; 计算机数据的流动决定了计算机的效率&#xff0c;数据流动就是数据在不同地方的来回拷贝。 所以我们会采取让硬件设备直接与CPU链接的方式&#xff0c;所以就有了储存器了。储存器大大提升了效…

AppScan 扫描工具及使用

一、简介 原名 watchire Appscan ,2007年被IBM收购&#xff0c;成为IBM Appscan。IBM AppScan是一款非常好用且功能强大的Web 应用安全测试工具 曾以 Watchfire AppScan 的名称享誉业界&#xff0c;Rational AppScan 可自动化 Web 应用的安全漏洞评估工作&#xff0c;能扫描和检…

新手Pytorch入门笔记-transforms.Compose()

我使用的图片是上图&#xff0c;直接下载即可 transforms.Compose 是PyTorch中的一个实用工具&#xff0c;用于创建一个包含多个数据变换操作的变换对象。这些变换操作通常用于数据预处理&#xff0c;例如图像数据的缩放、裁剪、旋转等。使用transforms.Compose 可以将多个数据…

【数据结构(邓俊辉)学习笔记】绪论05——动态规划

文章目录 0.前言1. Fibonacci数应用1.1 fib&#xff08;&#xff09;&#xff1a;递归1.1.1 问题与代码1.1.2 复杂度分析1.1.3 递归分析 1.2 fib&#xff08;&#xff09;&#xff1a;迭代 0.前言 make it work,make it right,make it fast. 让代码能够不仅正确而且足够高效地…

pytorch-激活函数与GPU加速

目录 1. sigmod和tanh2. relu3. Leaky Relu4. selu5. softplus6. GPU加速7. 使用GPU加速手写数据训练 1. sigmod和tanh sigmod梯度区间是0&#xff5e;1&#xff0c;当梯度趋近0或者1时会出现梯度弥散的问题。 tanh区间时-1&#xff5e;1&#xff0c;是sigmod经过平移和缩放而…

Golang | Leetcode Golang题解之第50题Pow(x,n)

题目&#xff1a; 题解&#xff1a; func myPow(x float64, n int) float64 {if n > 0 {return quickMul(x, n)}return 1.0 / quickMul(x, -n) }func quickMul(x float64, n int) float64 {if n 0 {return 1}y : quickMul(x, n/2)if n%2 0 {return y * y}return y * y * …

微服务组件-反向代理(Nginx)

微服务组件-反向代理(Nginx) Nginx 基本概念 1、nginx是什么&#xff1f; ①、Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器同时也提供了IMAP/POP3/SMTP服务。它是一款轻量级的Web服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&a…

Linux--内核移植(一)Kernel编译启动

Linux内核编译 编译内核之前需要先在ubuntu上安装lzop库&#xff0c;另外&#xff0c;图形化配置工具还需要ncurses库支持&#xff0c;安装命令为&#xff1a; sudo apt-get install lzop sudo apt-get install build-essential sudo apt-get install libncurses5-dev 在U…

大数据时代的引擎:大数据架构随记

大数据架构通常可以分为以下几层&#xff1a; 一、数据采集层 负责从各种数据源采集、清洗、转换、丰富以及格式化数据&#xff0c;可能包括结构化、半结构化和非结构化的数据。 1.1、常用的技术 在大数据领域&#xff0c;数据采集是一个关键的环节&#xff0c;常用的数据采集…

如何安装sbt(sbt在ubuntu上的安装与配置)(有详细安装网站和图解)

sbt下载官网 选择对应的版本和安装程序 Download | sbt (scala-sbt.org) 安装 解压 将sbt-1.9.0.tgz上传到xshell&#xff0c;并解压 解压&#xff1a; tar -zxvf sbt-1.9.0.tgz 配置 1、在/home/hadoop/sbt中创建sbt脚本 /home/hadoop/sbt 注意要改成自己的地址 cd …

【Linux】详解信号产生的方式

一、kill命令 在命令行中通过kill -数字 pid指令可以给指定进程发送指定信号。这里说明一下几个常见的信号&#xff1a; SIGINT&#xff08;2号信号&#xff09;&#xff1a;中断信号&#xff0c;通常由用户按下CtrlC产生&#xff0c;用于通知进程终止。SIGQUIT&#xff08;3号…

小型内衣裤洗衣机哪个牌子好?六大选购锦囊私藏分享

内衣洗衣机是现代家庭必不可少的小家电&#xff0c;它不仅方便快捷&#xff0c;还能够保持衣物清洁和卫生。然而&#xff0c;市场上洗衣机品牌众多&#xff0c;质量和性能参差不齐&#xff0c;使得消费者购买时难以做出选择。那么&#xff0c;小型内衣裤洗衣机哪个牌子好&#…

企业OA管理|基于SprinBoot+vue的企业OA管理系统(源码+数据库+文档)

企业OA管理目录 基于SprinBootvue的企业OA管理系统 一、前言 二、系统设计 三、系统功能设计 1 管理员模块的实现 1.1 用户信息管理 1.2 公告信息管理 1.3 客户关系管理 1.4 通讯录管理 2 用户模块的实现 2.1 客户关系添加 2.2 通讯录添加 2.3 日程安排添加 四、…