数据结构:双向链表

文章目录

  • 1. 双向带头循环链表的结构
  • 2. 相关操作
    • 2.1 创建节点
    • 2.2 尾插
    • 2.3 头插
    • 2.4 打印
    • 2.5 尾删
    • 2.6 头删
    • 2.7 查找
    • 2.8 指定位置前/后插入
    • 2.9 删除指定位置的节点
    • 2.10 删除指定位置后的节点
    • 2.11 销毁链表
  • 3.顺序表与链表区别

在这里插入图片描述

1. 双向带头循环链表的结构

在这里插入图片描述
与单链表不同的是:

  • 双向链表有一个“哨兵位”作为单独的头节点
  • 每个节点都可以指向其前驱和后继节点
  • 链表是循环的

带头链表里的头节点,实际为“哨兵位”,哨兵位节点不存储任何有效元素,只是站在这里“放哨的”
“哨兵位”存在的意义:遍历循环链表避免死循环。

typedef int ListDataType;
typedef struct List
{
	ListDataType data;
	struct List* prev;//指向前驱节点
	struct List* next;//指向后继节点
}List;

2. 相关操作

2.1 创建节点

在这里插入图片描述

  • 创建的每个节点应该自己成环
List* BuyNode(ListDataType x)
{
	List* newnode = (List*)malloc(sizeof(List));
	if (newnode == NULL)
	{
		perror(malloc);
		return NULL;
	}
	newnode->data = x;
	newnode->next = newnode;
	newnode->prev = newnode;
}

创建哨兵位:

int main()
{
	//哨兵位
	List* head = BuyNode(-1);
	return 0;
}

2.2 尾插

在这里插入图片描述

void ListPushBack(List* phead, ListDataType x)
{
	assert(phead);
	List* newnode = BuyNode(x);

	newnode->next = phead;
	newnode->prev = phead->prev;
	//尾节点指向新节点
	phead->prev->next = newnode;
	
	phead->prev = newnode;
}

2.3 头插

在这里插入图片描述

void ListPushFront(List* phead, ListDataType x)
{
	assert(phead);
	List* newnode = BuyNode(x);

	newnode->next = phead->next;
	newnode->prev = phead;
	phead->next = newnode;
	//只有头节点时,就是头节点的prev
	phead->next->prev = newnode;
}

2.4 打印

由于是循环链表,所以循环停止的条件应该是:cur != head

void ListPrint(List* phead)
{
	assert(phead);

	List* cur = phead->next;
	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

在这里插入图片描述

2.5 尾删

  • 不能没有节点
  • 尾节点的前驱节点指向头节点
  • 头节点指向尾节点的前驱节点
  • 释放尾节点
void ListPopBack(List* phead)
{
	assert(phead);
	//只有头节点
	assert(phead->next != phead);

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

2.6 头删

  • 不能没有节点
  • 待删除节点的后继节点的prev指向头节点
  • 头节点指向待删除节点的后继节点
void ListPopFront(List* phead)
{
	assert(phead);
	assert(phead->next != phead);

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

2.7 查找

List* ListFind(List* phead, ListDataType x)
{
	assert(phead);
	assert(phead->next != phead);
	List* cur = phead->next;

	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

2.8 指定位置前/后插入

  • 将新节点与指定位置相连即可

指定位置前

void ListPosFrontInsert(List* pos, ListDataType x)
{
	assert(pos);

	List* newnode = BuyNode(x);
	List* prev = pos->prev;

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

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

指定位置后

void ListPosBackInsert(List* pos, ListDataType x)
{
	assert(pos);

	List* newnode = BuyNode(x);
	List* next = pos->next;

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

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

2.9 删除指定位置的节点

  • 将指定位置的前驱节点、后继节点连接即可
void ListPosDel(List* pos)
{
	assert(pos);

	List* prev = pos->prev;
	List* next = pos->next;

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

2.10 删除指定位置后的节点

  • 若指定位置是尾节点,则应该执行头删
void ListPosAfterDel(List* phead, List* pos)
{
	assert(phead);
	assert(pos);
	if (pos->next == phead)
	{
		ListPopFront(phead);
	}
	else
	{
		List* del = pos->next;
		del->next->prev = pos;
		pos->next = del->next;
		free(del);
	}
}

2.11 销毁链表

  • 此接口的参数是一级指针,所以并不能将哨兵位销毁;因此需要再调用处再将哨兵位置为空
void ListDestroy(List* phead)
{
	assert(phead);
	assert(phead->next != phead);

	List* cur = phead->next;
	while (cur != phead)
	{
		List* next = cur->next;
		free(cur);
		cur = next;
	}
	cur = NULL;
	free(phead);
	phead = NULL;
}

在这里插入图片描述

3.顺序表与链表区别

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

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

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

相关文章

C#,奥西里斯数(Osiris Number)的算法与源代码

1 奥西里斯数(Osiris Number) 奥西里斯数(Osiris Number)是一个数字&#xff0c; 其值等于通过将其自身数字的所有排列相加而形成的所有数字的值之和。 计算结果&#xff1a; 2 源程序 using System; namespace Legalsoft.Truffer.Algorithm { /// <summary> /…

Django学习记录02

1.请求与响应 1.1get与post的区别 get 一般是从url输入地址&#xff0c;会调用get请求 post 一般是内部数据传输# get请求 def something(request):# req是一个对象&#xff0c;封装了用户发送过来的所有请求相关数据# 1.获取请求方式 http://localhost:8000/something# pri…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(二)

gnome-builder创建的程序&#xff0c;在工程树中有三个重要程序&#xff1a;main主程序、application应用程序和window主窗口程序。main整个程序的起始&#xff0c;它会操作application生产应用环境&#xff0c;application会操作window生成主窗口&#xff0c;于是就有了 appli…

【lesson47】进程通信之system V(共享内存)补充知识

文章目录 补充知识 补充知识 进行通信的key值问题&#xff0c;进程要通信的对方进程怎么能保证对方能看到&#xff0c;并且看到的就是该进程创建的共享内存的。 所以就通过key值来标识共享内存&#xff0c;key值是几不重要&#xff0c;只要在系统里是唯一的即可。 这样server和…

Java图形化界面编程——Container容器 笔记

2.3 Container容器 2.3.1 Container继承体系 Winow是可以独立存在的顶级窗口,默认使用BorderLayout管理其内部组件布局;Panel可以容纳其他组件&#xff0c;但不能独立存在&#xff0c;它必须内嵌其他容器中使用&#xff0c;默认使用FlowLayout管理其内部组件布局&#xff1b;S…

2月8日作业

1、现有文件test.c\test1.c\main.c,编写Makkefile 代码&#xff1a; CCgcc EXEa.out OBJS$(patsubst %.c,%.o,$(wildcard *.c)) CFLAGS-c -oall:$(EXE)$(EXE):$(OBJS)$(CC) $^ -o $%.o:%.c$(CC) $(CFLAGS) $ $^.PHONY:cleanclean:rm $(OBJS) $(EXE)运行结果&#xff1a; 2、…

【网络】:序列化和反序列化

序列化和反序列化 一.json库 二.简单使用json库 前面已经讲过TCP和UDP&#xff0c;也写过代码能够进行双方的通信了&#xff0c;那么有没有可能这种通信是不安全的呢&#xff1f;如果直接通信&#xff0c;可能会被底层捕捉&#xff1b;可能由于网络问题&#xff0c;一方只接收到…

云计算运营模式介绍

目录 一、云计算运营模式概述 1.1 概述 二、云计算服务角色 2.1 角色划分 2.1.1 云服务提供商 2.1.2 云服务消费者 2.1.3 云服务代理商 2.1.4 云计算审计员 2.1.5 云服务承运商 三、云计算责任模型 3.1 云计算服务模式与责任关系图 3.2 云计算服务模式与责任关系解析…

【LeetCode每日一题】525连续数组 303区域和检索(前缀和的基本概念和3个简单案例)

前缀和 // 构造prefix let prefix [0] arr.forEach(num > {prefix.push(prefix.at(-1) num); })如果想要计算某个区间 i 到 j 这个子数组的和时&#xff0c;可以根据 prefix[j1] - prefix[i] 获得。 例题1&#xff1a;303.区域和检索 - 数组不可变 给定一个整数数组 num…

深度神经网络中的BNN和DNN:基于存内计算的原理、实现与能量效率

前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff1a;https://www.captainbed.cn/z ChatGPT体验地址 文章目录 前言引言内存计算体系结构深度神经网络&#xff08;DNN&#xff09;随机梯度的优…

C++进阶(十三)异常

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、C语言传统的处理错误的方式二、C异常概念三、异常的使用1、异常的抛出和捕获2、异常的重新…

【Cocos入门】场景切换(loadScene、preloadScene)

一、loadScene 加载场景 loadScene(sceneName: string, onLaunched: Director.OnSceneLaunched, onUnloaded: Director.OnUnload) : boolean 通过场景名称进行加载场景。返回值为布尔类型 参数&#xff1a; NameTypeDescriptionsceneNamestring场景名称onLaunchedDirector.O…

FPGA_工程_按键控制的基于Rom数码管显示

一 信号 框图&#xff1a; 其中 key_filter seg_595_dynamic均为已有模块&#xff0c;直接例化即可使用&#xff0c;rom_8*256模块&#xff0c;调用rom ip实现。Rom_ctrl模块需要重新编写。 波形图&#xff1a; 二 代码 module key_fliter #(parameter CNT_MAX 24d9_999_99…

基于微信小程序的新生报到系统的研究与实现,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

分布式springboot 3项目集成mybatis官方生成器开发记录

文章目录 说明实现思路实现步骤第一步&#xff1a;创建generator子模块第二步&#xff1a;引入相关maven插件和依赖第三步&#xff1a;编写生成器配置文件第四步&#xff1a;运行查看结果 说明 该文章为作者开发学习记录&#xff0c;方便以后复习和交流主要内容为&#xff1a;…

Zoho Mail企业邮箱商业扩展第3部分:计算财务状况

在Zoho Mail商业扩展系列的压轴篇章中&#xff0c;王雪琳利用Zoho Mail的集成功能成功地完成了各项工作&#xff0c;并顺利地建立了自己的营销代理机构。让我们快速回顾一下她的成功之路。 一、使用Zoho Mail成功方法概述 首先她通过Zoho Mail为其电子邮件地址设置了自定义域…

如何开始深度学习,从实践开始

将“如何开始深度学习”这个问题喂给ChatGPT和文心一言&#xff0c;会给出很有专业水准的答案&#xff0c;比如&#xff1a; 要开始深度学习&#xff0c;你可以遵循以下步骤&#xff1a; 学习Python编程语言的基础知识&#xff0c;因为它在深度学习框架中经常被使用。 熟悉线性…

2023年出版的新书中提到的《人月神话》(202402更新)(2)共8本

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 《人月神话》于1975年出版&#xff0c;1995年出二十周年版。自出版以来&#xff0c;该书被大量的书籍和文章引用&#xff0c;直到现在热潮不退。 2023年&#xff0c;清华大学出版社推…

嵌入式学习之Linux入门篇笔记——13,Linux第一个程序HelloWorld

配套视频学习链接&#xff1a;http://【【北京迅为】嵌入式学习之Linux入门篇】 https://www.bilibili.com/video/BV1M7411m7wT/?p4&share_sourcecopy_web&vd_sourcea0ef2c4953d33a9260910aaea45eaec8 1.什么是 gcc&#xff1f; gcc 全称&#xff08;gun compiler…

【Docker进阶】镜像制作-用Dockerfile制作镜像(一)

进阶一 docker镜像制作 文章目录 进阶一 docker镜像制作用dockerfile制作镜像dockerfile是什么dockerfile格式为什么需要dockerfileDockerfile指令集合FROMMAINTAINERLABELCOPYENVWORKDIR 用dockerfile制作镜像 用快照制作镜像的缺陷&#xff1a; 黑盒不可重复臃肿 docker…