数据结构 | 带头双向循环链表专题

数据结构 | 带头双向循环链表专题

前言

前面我们学了单链表,我们这次来看一个专题带头的双向循环链表~~

文章目录

  • 数据结构 | 带头双向循环链表专题
  • 前言
    • 带头双向循环链表的结构
    • 实现双向链表
      • 头文件的定义
      • 哨兵位初始化
      • 创建节点
      • 尾插
      • 尾删
      • 头插
      • 头删
      • 打印
      • 查找
      • 指定位置前插入
      • 删除指定位置
      • 销毁链表
      • List.h
      • List.c

带头双向循环链表的结构

在这里插入图片描述

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

哨兵位存在的意义:

  • 遍历循环链表避免死循环。

  • 每个节点有三部分内容:
    • 保存数据的val
    • 保存下一个节点的地址next指针
    • 保存前一个节点的地址prev指针

实现双向链表

我们需要实现以下功能

头文件的定义

List.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _val;struct ListNode* _next;struct ListNode* _prev;
}ListNode;//哨兵位初始化
ListNode* ListInit();// 创建返回链表的头结点.
ListNode* ListCreate(ListNode* pHead);
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

哨兵位初始化

  • 这里我们初始化成-1代表是哨兵位,然后让前驱指针和next指针指向自己
ListNode* ListInit()
{ListNode* pHead = ListCreate(-1);pHead->_next = pHead;pHead->_prev = pHead;return pHead;
}

创建节点

  • malloc一个节点,然后让新节点的next和prev赋值为空,将值给了val,最后返回空间
ListNode* ListCreate(LTDataType* x)
{ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));if (newnode == NULL){perror("malloc fail!\n");exit(-1);}newnode->_val = x;newnode->_next = NULL;newnode->_prev = NULL;return newnode;
}

尾插

void ListPushBack(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* tail = pHead->_prev;ListNode* newnode = ListCreate(x);// phead   tail   newnodetail->_next = newnode;newnode->_prev = tail;newnode->_next = pHead;pHead->_prev = newnode;
}

尾删

void ListPopBack(ListNode* pHead)
{assert(pHead);assert(pHead->_next != pHead);ListNode* tail = pHead->_prev;ListNode* tailprev = tail->_prev;free(tail);tailprev->_next = pHead;pHead->_prev = tailprev;
}

头插

void ListPushFront(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead->_next;ListNode* newnode = ListCreate(x);pHead->_next = newnode;newnode->_prev = pHead;newnode->_next = cur;cur->_prev = newnode;
}

头删

void ListPopFront(ListNode* pHead)
{assert(pHead);assert(pHead->_next != pHead);ListNode* first = pHead->_next;ListNode* second = first->_next;pHead->_next = second;second->_prev = pHead;free(first);first = NULL;
}

打印

void ListPrint(ListNode* pHead)
{ListNode* tail = pHead->_next;printf("哨兵位->");while (tail != pHead){printf("%d->", tail->_val);tail = tail->_next;}printf("NULL\n");
}

查找

ListNode* ListFind(ListNode* pHead, LTDataType x)
{assert(pHead);ListNode* cur = pHead->_next;while (cur != pHead){if (cur->_val == x){return cur;}cur = cur->_next;}return NULL;
}

指定位置前插入

void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newnode = ListCreate(x);ListNode* prev = pos->_prev;prev->_next = newnode;newnode->_prev = prev;pos->_prev = newnode;newnode->_next = pos;
}

删除指定位置

void ListErase(ListNode* pos)
{assert(pos);ListNode* prev = pos->_prev;ListNode* next = pos->_next;prev->_next = next;next->_prev = prev;free(pos);pos = NULL;
}

销毁链表

void ListDestory(ListNode* pHead)
{assert(pHead);ListNode* cur = pHead->_next;while (cur != pHead){ListNode* next = cur->_next;free(cur);cur = next;}free(pHead);pHead = NULL;
}

List.h

#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int LTDataType;typedef struct ListNode {LTDataType val;struct ListNode* next;struct ListNode* prev;
}LTNode;//初始化哨兵位
LTNode* LTInit();
//创建节点
LTNode* LTcreate(LTDataType x);
//销毁
void LTDestroy(LTNode* phead);
//打印
void LTPrint(LTNode* phead);
//判断是否为空
bool LTEmpty(LTNode* phead);
//尾插
void LTPushBack(LTNode* phead, LTDataType x);
//尾删
void LTPopBack(LTNode* phead);
//头插
void LTPushFront(LTNode* phead, LTDataType x);
//头删
void LTPopFront(LTNode* phead);
//指定位置插入
void LTInsert(LTNode* pos, LTDataType x);
//指定位置删除
LTNode* LTErase(LTNode* pos);
//查找
LTNode* LTFind(LTNode* phead, LTDataType x);

List.c

#define _CRT_SECURE_NO_WARNINGS 1#include"List.h"//初始化哨兵位
LTNode* LTInit()
{LTNode* phead = LTcreate(-1);phead->next = phead;phead->prev = phead;return phead;
}
//创建节点
LTNode* LTcreate(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror("malloc fail!\n");exit(-1);}newnode->next = NULL;newnode->prev = NULL;newnode->val = x;return newnode;
} //销毁
void LTDestroy(LTNode* phead);
//打印
void LTPrint(LTNode* phead)
{assert(phead);printf("哨兵位<->");LTNode* cur = phead->next;while (cur != phead){printf("%d<->", cur->val);cur = cur->next;}printf("\n");
}
//判断是否为空
bool LTEmpty(LTNode* phead)
{assert(phead);return phead->val == 0;
}//尾插
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTcreate(x);LTNode* tail = phead->prev;tail->next = newnode;newnode->prev = tail;newnode->next = phead;phead->prev = newnode;
}//尾删
void LTPopBack(LTNode* phead)
{assert(phead);LTNode* tail = phead->prev;LTNode* tailprev = tail->prev;free(tail);tail = NULL;tailprev->next = phead;phead->prev = tailprev;
}
//头插
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);LTNode* newnode = LTcreate(x);LTNode* cur = phead->next;phead->next = newnode;newnode->prev = phead;cur->prev = newnode;newnode->next = cur;
}
//头删
void LTPopFront(LTNode* phead)
{assert(phead);LTNode* first = phead->next;LTNode* second = first->next;free(first);phead->next = second;second->prev = phead;
}//查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}//指定位置的前面插入
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newnode = LTcreate(x);LTNode* posprev = pos->prev;posprev->next = newnode;newnode->prev = posprev;newnode->next = pos;pos->prev = newnode;}
//指定位置删除
LTNode* LTErase(LTNode* pos)
{assert(pos);LTNode* posprev = pos->prev;LTNode* posnext = pos->next;free(pos);posprev->next = posnext;posprev = posprev;
}
  • 顺序表和双向链表的分析
不同点顺序表链表(单链表)
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

好了,双向链表就到这里结束了,有用的话点个赞吧~~

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DVWA - 3

文章目录 XSS&#xff08;Dom&#xff09;lowmediumhighimpossible XSS&#xff08;Dom&#xff09; XSS 主要基于JavaScript语言进行恶意攻击&#xff0c;常用于窃取 cookie&#xff0c;越权操作&#xff0c;传播病毒等。DOM全称为Document Object Model&#xff0c;即文档对…

Linux-AWK(应用最广泛的文本处理程序)

目录 一、awk基础 二、awk工作原理 三、OFS输出分隔符 四、awk的格式化输出 五、awk模式pattern 一、awk基础 使用案例&#xff1a; 1.准备工作 请在Linux中执行以下指令 cat -n /etc/passwd > ./passwd 练习&#xff1a; 1.从文件 passwd 中提取并打印出第五行的内…

微软允许OEM对Win10不提供关闭Secure Boot

用户可能将无法在Windows 10电脑上安装其它操作系统了&#xff0c;微软不再要求OEM在UEFI 中提供的“关闭 Secure Boot”的选项。 微软最早是在Designed for Windows 8认证时要求OEM的产品必须支持UEFI Secure Boot。Secure Boot 被设计用来防止恶意程序悄悄潜入到引导进程。问…

Linux编辑器:vim的简单介绍及使用

目录 1.什么是vim 2.vim的基本概念 3.vim 的基本操作 4. 各模式下的命令集 4.1 正常模式命令集 4.2 末行模式命令集 5.补充 5.1 vim支持多文件编辑 5.2 vim 的配置 1.vim 配置原理 2. 常用简单配置选项&#xff1a; 3. 使用插件 1.什么是vim Vim 是从 vi 发展出…

AWS实战(一)-创建S3 存储桶

1&#xff09;登录AWS账号&#xff0c;选择服务—>存储—>S3。 2&#xff09;查看存储桶列表 3&#xff09;点击"创建存储桶"创建bucket。 4&#xff09;设置跨域 点击编辑&#xff0c;修改跨域设置即可。

【Seata源码学习 】 扫描@GlobalTransaction注解 篇一

1. SeataAutoConfiguration 自动配置类的加载 基于SpringBoot的starter机制&#xff0c;在应用上下文启动时&#xff0c;会加载SeataAutoConfiguration自动配置类 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfigurationio.seata.spring.boot.aut…

Linux之基本指令操作

1、whoami whoami&#xff1a;查看当前账号是谁 2、who who&#xff1a;查看当前我的系统当中有哪些用户&#xff0c;当前有哪些人登录了我的机器 3、 pwd pwd&#xff1a;查看我当前所处的目录&#xff0c;就好比Windows下的路径 4、ls ls&#xff1a;查看当前目录下的文件信…

[文件读取]Druid 任意文件读取 (CVE-2021-36749)

1.1漏洞描述 漏洞编号CVE-2021-36749漏洞类型文件读取漏洞等级⭐⭐⭐漏洞环境VULFOCUS攻击方式 描述: 由于用户指定 HTTP InputSource 没有做出限制&#xff0c;可以通过将文件 URL 传递给 HTTP InputSource 来绕过应用程序级别的限制。攻击者可利用该漏洞在未授权情况下&…

C++模拟实现——红黑树

一、介绍 红黑树也是对一般的搜索二叉树不能保证平衡的一个改进&#xff0c;和AVL树采用的思路不同&#xff0c;但同样需要旋转&#xff0c;其本质也是一颗平衡搜索二叉树&#xff0c;其节点有颜色的区分&#xff0c;并且被一些规则束缚&#xff0c;在这些规则下&#xff0c;能…

sqli-labs关卡13(基于post提交的单引号加括号的报错盲注)通关思路

文章目录 前言一、回顾第十二关知识点二、靶场第十三关通关思路1、判断注入点2、爆显位3、爆数据库名4、爆数据库表5、爆数据库列6、爆数据库关键信息 总结 前言 此文章只用于学习和反思巩固sql注入知识&#xff0c;禁止用于做非法攻击。注意靶场是可以练习的平台&#xff0c;…

K8S知识点(九)

&#xff08;1&#xff09;Pod详解-结构和定义 一级属性有下面这些&#xff1a;前两个属性是字符串&#xff0c;上面有定义 kind&#xff1a;Pod version&#xff1a;v1 下面的属性是object 还可以继续查看子属性&#xff1a;二级属性 还可以继续查看三级属性&#xff1a; 通…

Exploration by random network distillation论文笔记

Exploration by Random Network Distillation (2018) 随机网络蒸馏探索 0、问题 这篇文章提出的随机网络蒸馏方法与Curiosity-driven Exploration by Self-supervised Prediction中提出的好奇心机制的区别&#xff1f; 猜想&#xff1a;本文是基于随机网络蒸馏提出的intrin…