【C数据结构】带头双向循环链表_HDList

news2024/10/5 17:54:55

目录

带头双向循环链表_HDList

【1】链表概念

【2】链表分类

【3】带头双向循环链表

【3.1】带头双向循环链表数据结构与接口定义

【3.2】带头双向循环链表初始化

【3.3】带头双向循环链表开辟节点空间

【3.4】带头双向循环链表销毁

【3.5】带头双向循环链表头插

【3.6】带头双向循环链表尾插

【3.7】 带头双向循环链表头删

【3.8】 带头双向循环链表尾删

【3.9】带头双向循环链表在pos位置插

【3.10】带头双向循环链表在pos位置删

【3.11】 无头单向非循环链表查找

【3.12】 无头单向非循环链表返回大小

【3.13】 无头单向非循环链表打印

【3.14】 无头单向非循环链表判空


带头双向循环链表_HDList

【1】链表概念

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

        链表是指逻辑结构上一个挨一个的数据,在实际存储时,并没有像顺序表那样也相互紧挨着。恰恰相反,数据随机分布在内存中的各个位置。

        由于分散存储,为了能够体现出数据元素之间的逻辑关系,每个数据元素在存储的同时,要配备一个指针,用于指向它的直接后继元素,即每一个数据元素都指向下一个数据元素(最后一个指向NULL(空))。

        如图所示,当每一个数据元素都和它下一个数据元素用指针链接在一起时,就形成了一个链,这个链子的头就位于第一个数据元素,这样的存储方式就是链式存储。

【2】链表分类

  • 单向或者双向链表

  • 带头或不带头链表

  • 循环非循环链表

  • 虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:

【3】带头双向循环链表

双向链表特点: 

  • 每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 实现起来要困难一些
  • 相对于单向链表, 必然占用内存空间更大一些.
  • 既可以从头遍历到尾, 又可以从尾遍历到头

双向链表的定义: 

        双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

下图为双向链表的结构图。

【3.1】带头双向循环链表数据结构与接口定义

链表中存放的不是基本数据类型,需要用结构体实现自定义:

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
​
// 双向带头循环链表中数据元素的构成
typedef int DHLDataType;
typedef struct DHListNode
{
    DHLDataType data; // 数据存储区、
    struct DHListNode* prev;    // 记录上一个节点。
    struct DHListNode* next;    // 记录下一个节点。
}DHListNode;
​
// 双向带头循环链表 - 初始化函数声明。
DHListNode* DHListInit();
​
// 双向带头循环链表 - 开辟节点函数声明。
DHListNode* BuyDHListNode(DHLDataType val);
​
// 双向带头循环链表 - 内存销毁函数声明。
void DHListDestory(DHListNode* pHead);
​
// 双向带头循环链表 - 尾插函数声明。
void DHListPushBack(DHListNode* pHead, DHLDataType val);
​
// 双向带头循环链表 - 头插函数声明。
void DHListPushFront(DHListNode* pHead, DHLDataType val);
​
// 双向带头循环链表 - 尾删函数声明。
void DHListPopBack(DHListNode* pHead);
​
// 双向带头循环链表 - 头删函数声明。
void DHListPopFront(DHListNode* pHead);
​
// 双向带头循环链表 - 在任意位置前插函数声明。
void DHListInsert(DHListNode* pos, DHLDataType val);
​
// 双向带头循环链表 - 在任意位置前删函数声明。
void DHListErase(DHListNode* pos);
​
// 双向带头循环链表 - 打印函数声明。
void DHListPrint(DHListNode* pHead);
​
// 双向带头循环链表 - 判断链表是否为空函声明。
bool DHListEmpty(DHListNode* pHead);
​
// 双向带头循环链表 - 统计节点个数空函数声明。
size_t DHListSize(DHListNode* pHead);
​
// 双向带头循环链表 - 查找节点函数声明。
DHListNode* DHListFind(DHListNode* pHead, DHLDataType val);

【3.2】带头双向循环链表初始化

// 双向带头循环链表 - 初始化函数。
DHListNode* DHListInit()
{
    DHListNode* newNode = (DHListNode*)malloc(sizeof(DHListNode));
    // 判断是否开辟内存空间成功。
    if (newNode == NULL)
    {
        perror("ListInit malloc fail!");
        exit(-1);
    }
​
    // 程序走到这里说明开辟空间成功了。
    newNode->prev = newNode;
    newNode->next = newNode;
    newNode->data = 0;
    return newNode;
}

【3.3】带头双向循环链表开辟节点空间

// 双向带头循环链表 - 开辟节点函数。
DHListNode* BuyDHListNode(DHLDataType val)
{
    DHListNode* newNode = (DHListNode*)malloc(sizeof(DHListNode));
    // 判断是否开辟内存空间成功。
    if (newNode == NULL)
    {
        perror("ListInit malloc fail!");
        exit(-1);
    }
​
    // 程序走到这里说明开辟空间成功了。
    newNode->prev = NULL;
    newNode->next = NULL;
    newNode->data = val;
    return newNode;
​
}

【3.4】带头双向循环链表销毁

// 双向带头循环链表 - 内存销毁函数声明。
void DHListDestory(DHListNode* pHead)
{
    // 断言保护形参指针不为空。
    assert(pHead);
​
    // 遍历一个一个的进行销毁。
    DHListNode* pBegin = pHead->next;
    while (pBegin != pHead)
    {
        DHListNode* next = pBegin->next;
        free(pBegin);
        pBegin = NULL;
​
        // 迭代
        pBegin = next;
    }
​
    // 其实这里是无效的,因为传递的是一级指针,外部调用完进行置NULL;
    pHead = NULL;
}

【3.5】带头双向循环链表头插

// 双向带头循环链表 - 头插函数。
void DHListPushFront(DHListNode* pHead, DHLDataType val)
{   
    // 断言保护形参指针不为空。
    assert(pHead);
​
    /*
    // 开辟新的节点空间。
    DHListNode* newNode = BuyDHListNode(val);
​
    // 链接链表 - 指针交换。
    DHListNode* next = pHead->next;
​
    pHead->next = newNode;
    newNode->prev = pHead;
​
    newNode->next = next;
    next->prev = newNode;
    */
​
    DHListInsert(pHead->next, val);
}

【3.6】带头双向循环链表尾插

// 双向带头循环链表 - 尾插函数。
void DHListPushBack(DHListNode* pHead, DHLDataType val)
{
    // 断言保护形参指针不为空。
    assert(pHead);
​
    /*
    // 开辟新的节点空间。
    DHListNode* newNode = BuyDHListNode(val);
​
    // 链接链表 - 指针交换。
    DHListNode* pTail = pHead->prev;
    pTail->next = newNode;
​
    newNode->next = pHead;
    newNode->prev = pTail;
​
    pHead->prev = newNode;
    */
​
    DHListInsert(pHead, val);
}

【3.7】 带头双向循环链表头删

​
// 双向带头循环链表 - 头删函数。
void DHListPopFront(DHListNode* pHead)
{
    // 断言保护形参指针不为空,判断链表不为空。
    assert(pHead);
    assert(!DHListEmpty(pHead));
​
    /*
    DHListNode* pFirst = pHead->next;
    DHListNode* pSecond = pFirst->next;
​
    // 修改链接。
    pHead->next = pSecond;
    pSecond->prev = pHead;
    // 释放掉保留的最后一个节点。
    free(pFirst);
    pFirst = NULL;
    */
​
    DHListErase(pHead->next);
}

【3.8】 带头双向循环链表尾删

// 双向带头循环链表 - 尾删函数。
void DHListPopBack(DHListNode* pHead)
{
    // 断言保护形参指针不为空,判断链表不为空。
    assert(pHead);
    assert(!DHListEmpty(pHead));
​
    /*
    DHListNode* pTail = pHead->prev;
    DHListNode* pFirst = pTail->prev;
​
    // 修改链接。
    pHead->prev = pFirst;
    pFirst->next = pHead;
    // 释放掉保留的最后一个节点。
    free(pTail);
    pTail = NULL;
    */
​
    DHListErase(pHead->prev);
}

【3.9】带头双向循环链表在pos位置插

// 双向带头循环链表 - 在任意位置前插函数声明。
void DHListInsert(DHListNode* pos, DHLDataType val)
{
    // 断言保护形参指针不为空。
    assert(pos);
​
    // 定位置和开辟新节点。
    DHListNode* prev = pos->prev;
    DHListNode* newNode = BuyDHListNode(val);
​
    // 链接
    prev->next = newNode;
​
    newNode->prev = prev;
    newNode->next = pos;
​
    pos->prev = newNode;
​
}

【3.10】带头双向循环链表在pos位置删

// 双向带头循环链表 - 在任意位置前删函数声明。
void DHListErase(DHListNode* pos)
{
    // 断言保护形参指针不为空。
    assert(pos);
​
    // 定位置
    DHListNode* prev = pos->prev;
    DHListNode* next = pos->next;
​
    // 链接
    prev->next = next;
    next->prev = prev;
    //销毁pos节点
    free(pos);
    pos = NULL;
}

【3.11】 无头单向非循环链表查找

// 双向带头循环链表 - 查找节点函数。
DHListNode* DHListFind(DHListNode* pHead, DHLDataType val)
{
    // 断言保护形参指针不为空。
    assert(pHead);
​
    // 遍历所有节点统计大小
    DHListNode* pBegin = pHead->next;
    while (pBegin != pHead)
    {
        if (pBegin->data == val)
        {
            return pBegin;
        }
​
        pBegin = pBegin->next;
    }
    // 返回NULL
    return NULL;
}

【3.12】 无头单向非循环链表返回大小

// 双向带头循环链表 - 统计节点个数空函数。
size_t DHListSize(DHListNode* pHead)
{
    // 断言保护形参指针不为空。
    assert(pHead);
​
    // 遍历所有节点统计大小
    DHListNode* pBegin = pHead->next;
    size_t count = 0;
    while (pBegin != pHead)
    {
        count++;
        pBegin = pBegin->next;
    }
    // 返回个数
    return count;
}

【3.13】 无头单向非循环链表打印

// 双向带头循环链表 - 打印函数。
void DHListPrint(DHListNode* pHead)
{
    // 断言保护形参指针不为空。
    assert(pHead);
    // 打印一个头作为标志。
    printf("pHead");
​
    // 遍历节点打印数据
    DHListNode* pBegin = pHead->next;
    while (pBegin != pHead)
    {
        printf("<-%d->",pBegin->data);
        pBegin = pBegin->next;
    
    }
    printf("pHead");
    printf("\n");
}

【3.14】 无头单向非循环链表判空

// 双向带头循环链表 - 判断链表是否为空函数。
bool DHListEmpty(DHListNode* pHead)
{
    // 断言保护形参指针不为空。
    assert(pHead);
​
    // 判断链表是不是NULL链表。
    return pHead->next == pHead;
}

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

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

相关文章

【C数据结构】带头单向非循环链表_HList

目录 带头单向非循环链表_HList 【1】链表概念 【2】链表分类 【3】有头单向非循环链表 【3.1】非循环链表数据结构与接口定义 【3.2】带头单向非循环链表初始化 【3.3】带头单向非循环链表释放空间 【3.4】带头单向非循环链表创建节点 【3.5】带头单向非循环链表头插…

HTML学习(二)

视频 <video width"320" height"240" controls> <source src"movie.mp4" type"video/mp4"> <source src"movie.ogg" type"video/ogg"> </video> 音频 <audio controls> <…

C++【AVL树】

✨个人主页&#xff1a; 北 海 &#x1f389;所属专栏&#xff1a; C修行之路 &#x1f383;操作环境&#xff1a; Visual Studio 2019 版本 16.11.17 文章目录 &#x1f307;前言&#x1f3d9;️正文1、认识AVL树1.1、AVL树的定义 2、AVL树的插入操作2.1、抽象图2.2、插入流程…

控制层调用接口的http请求封装

目录 0.碎碎念1.controller层2.util层3.测试3.1中间层调用GET请求3.2中间层调用POST请求 0.碎碎念 因为只是为了写这个帮助类&#xff0c;解耦&#xff0c;不敢拿已经写了一堆的代码改&#xff0c;就单独拆了个项目出来&#xff0c;持久层全是mybatisplus生成的。     所以…

Kafka源码解析之索引

Kafka源码解析之索引 索引结构 Kafka有两种类型的索引&#xff1a; TimeIndex: 根据时间戳索引&#xff0c;可以通过时间查找偏移量所在位置&#xff0c;目录下以.timeindex结尾Index: 根据偏移量索引&#xff0c;.index结尾 构建索引时机 由log.index.interval.bytes 参…

3. redis cluster集群运维与核心原理剖析

分布式缓存技术Redis 1. Redis集群方案比较2. Redis高可用集群搭建 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地方&#xff0c;还望大佬多多指点纠正&#xff0c;勿喷。 课程内容&#xff1a; 1、哨兵集群与Redis Cluster架构异同 2、Redis高可用集群快速实…

2023/6/18总结

JS 在document.querySelectorAll(CSS选择器) 选到的集合并没有pop()和push()等数组的方法。是一个伪数组。 如果想要得到里面的每一个对象&#xff0c;需要用for遍历获得 document.getElementById(id名称) 根据id获取一个元素 document.getElementsByTagName(标签名字) 根…

Css面试题:css文字隐藏

文章目录 文字隐藏单行文字隐藏多行文字隐藏基于高度设置多行文字隐藏基于行数设置多行文字隐藏 文字隐藏 单行文字隐藏 主要是通过overflow&#xff0c;text-overflow&#xff0c;white-space三个属性实现。 overflow&#xff1a;visible|hidden|auto|scroll|inherit&#…

【c语言】-- 操作符汇总

&#x1f4d5;博主介绍&#xff1a;目前大一正在学习c语言&#xff0c;数据结构&#xff0c;计算机网络。 c语言学习&#xff0c;是为了更好的学习其他的编程语言&#xff0c;C语言是母体语言&#xff0c;是人机交互接近底层的桥梁。 本章来学习数组。 让我们开启c语言学习之旅…

简单认识web与http协议

文章目录 web基础域名概述DNS&#xff08;Domain Name System域名系统&#xff09; 域名空间结构 域名实际用法 2. 网页的概念2.1 网页&#xff08;HTTP/HTTPS&#xff09;HTML 概述HTML超文本标记语言 HTML文档的结构头标签中常用标签内容标签中常用标签Web概述具体组成web的主…

chatgpt赋能python:Python如何创建窗口——从入门到精通

Python如何创建窗口——从入门到精通 Python是一种高级编程语言&#xff0c;它的易读性和清晰简洁的语法使它成为许多人喜欢学习的编程语言之一。Python的一个主要特色是其丰富的库和模块。在本文中&#xff0c;我们将讨论如何使用Python创建一个窗口&#xff0c;并在其中添加…

【力扣刷题 | 第十一天】

前言&#xff1a; 我将会利用几天把树的经典例题都刷完&#xff0c;希望我可以坚持下去。 226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 解题思路&#xff1a;我们交换每一…

C语言之运算符用法(补充前面运算符中的不足)

设定&#xff1a;int X20,Y10 1、算术运算符 注&#xff1a;自增和自减运算符只能用于变量&#xff0c;不可用于常量或表达式。另&#xff0c;X与X是不同的(–亦同)。以语句a[x]100;为例&#xff1a; a[X]100;执行之后得到&#xff1a;a[20] 100、X 21。//即&#xff0c;先执行…

Windows10下超详细Mysql安装

目录 0. 前言1. 下载mysql2. 开始安装3. 验证安装4. 环境变量配置 0. 前言 Mysql简介&#xff1a; MySQL是一种开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它使用SQL&#xff08;结构化查询语言&#xff09;语言进行数据的存储和访问。MySQL的设计…

git版本管理入门(本地/远程仓库,常用命令)

目录 git简介 安装git 配置SSH key Linux环境下需要命令生成ssh key 本地git管理 多人协作流程 追加 重新提交 git命令 git commit本地和git push远程 git stash和git stash pop暂存 git status查看修改哪些了文件​ git diff 查看修改前后的差异 git log查看提交…

Centos7安装配置Docker

1. 什么是Docker 在开篇之前考虑到阅读人群,我觉得有必要向各位读者朋友简单介绍一下Docker是什么,它解决了什么问题&#xff1f;Docker是基于Go语言实现的云开源项目。它对此给出了一个标准化的解决方案-----系统平滑移植&#xff0c;容器虚拟化技术。让开发者可以打包他们的…

从加密到签名:如何使用Java实现高效、安全的RSA加解密算法?

目录 1. 接下来让小编给您们编写实现代码&#xff01;请躺好 ☺ 1.1 配置application.yml文件 1.2 RSA算法签名工具类 1.3 RSA算法生成签名以及效验签名测试 1.4 RSA算法生成公钥私钥、加密、解密工具类 1.5 RSA算法加解密测试 我们为什么要使用RSA算法来进行加解密&…

React之state详解

目录 执行过程 异步 React18与自动批处理 setState 推荐用法 ()>{return }&#xff0c;this.state. 生命周期 数据没改变时​不渲染 shouldComponentUpdate PureComponent自动&#xff08;推荐&#xff09; 你真的理解setState吗&#xff1f; - 掘金 组件的私有…

《Nature Aging》: 揭示皮肤衰老的分子机制

一个人衰老最直接的体现就是皮肤衰老。人体的皮肤一般从25&#xff5e;30岁以后即随着年龄的增长而逐渐衰老&#xff0c;大约在35&#xff5e;40岁后逐渐出现比较明显的衰老变化。但是&#xff0c;我们的皮肤为什么会衰老呢&#xff1f;要回答这个问题&#xff0c;我们首先要了…

STC单片机存储器介绍和使用

STC单片机存储器介绍和使用 🌿STC15F2K60S2系列内部结构框图 🌿STC12C5A60S2系列内部结构框图 📑程序存储器(ROM/Flash) 🔖STC单片机ROM容量大小可以根据其型号和命名规则了解到。 🌿STC