[C语言实现]带你手撕带头循环双链表

数据结构----带头循环双链表(带哨兵位)(C语言) 双链表作为数据结构中的非线性表结构,对比于单链表具有它的优势:可以直接找到当前结点的上下结点。但由于双链表的这个特性,如果不带哨兵位,一不小心就可能手撕一个错的双链表出来。所以今天我们写一个带哨兵位的循环双链表。非循环双链表的话,实现起来和循环双链表差别不大,只是头尾并没有相连,而是指向NULL。今天,我将手撕一个双链表,包括双链表的初始化,插入删除,查找等一系列操作,准备好了吗!!!先来一个抽象图,理解理解。来了! 阅读详情


目录

什么是双链表?

带头结点的优势:

双链表的实现:


什么是循环双链表?

众所周知,顺序表的插入和删除有时候需要大量移动数据,并且每次开辟空间都可能会浪费大量内存和CPU资源,于是我们有了链表,我们之前已经实现了单链表,我们可以发现单链表的一个劣势——每次查找都必须从头结点开始,并且这是一个不可逆的过程,你不可以通过下一个结点找到前一个,只能通过前一个结点找到下一个。

我们将链表的结点空间不再一分为二,而是一分为三,分别是:存储数据的空间,存储下一个结点地址的空间,存储上一个结点地址的空间。这样我们就可以通过一个结点找到它的上一个结点和下一个结点了。

最后我们再将首结点的prev指向尾结点,尾结点的next指向首结点,就变成了循环双链表。

我们在此基础上,再为这个结构加入头结点。

带头结点的优势:

头结点就像是一个哨兵,一直站在第一个位置,它的data域不存储任何值,next域指向第一个有效的链表节点,prev域指向链表的尾结点。

我们为什么需要头结点?

1. 头结点可以让插入操作变得更加方便,不需要分有无结点的情况讨论,因为永远存在一个头结点

2. 头结点可以让我们函数传参的时候不需要传二级指针,因为此时不需要改变head本身。

此外还有其他优点,在之后的代码实现中我们会慢慢体会到头结点的优势。


双链表的代码实现+详细注释:

头文件:

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

LTNode* ListNodeInit(LTNode* phead);//初始化
void LTPushBack(LTNode* phead, LTDataType x);//尾插
void LTPushFront(LTNode* phead, LTDataType x);//头插
void LTPopFront(LTNode* phead);//头删
void LTPopBack(LTNode* phead);//尾删
void LTPrint(LTNode* phead);//打印 
void LTInsert(LTNode* pos, LTDataType x);//pos前一个位置的插入
void LTErase(LTNode* pos);//pos当前位置的删除
LTNode* LTFind(LTNode* phead, LTDataType x);//查找
LTNode* LTDestroy(LTNode* phead);//销毁

函数实现:

//List.c
#include "List.h"
LTNode* ListNodeInit(LTNode* phead)
{
    //创建头结点
    LTNode* head = (LTNode*)malloc(sizeof(LTNode));
    //防止内存开辟失败
    assert(head);
    //初始化头结点
    head->prev = head->next = head;
    //返回头结点
    return head;
}
void LTPushBack(LTNode* phead, LTDataType x)
{
    //断言 防止用户传入空指针
    assert(phead);
    //尾插
    LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
    assert(newnode);
    newnode->data = x;
    //找到尾结点
    LTNode* tail = phead->prev;
    //开始插入
    newnode->next = phead;
    phead->prev = newnode;
    tail->next = newnode;
    newnode->prev = tail;
}
void LTPushFront(LTNode* phead, LTDataType x)
{
    //断言
    assert(phead);
    //头插
    LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
    assert(newnode);
    newnode->data = x;
    //找到第一个有效节点
    LTNode* Next = phead->next;
    //开始插入
    Next->prev = newnode;
    newnode->next = Next;
    phead->next = newonde;
    newnode->prev = phead;
}
void LTPopBack(LTNode* phead)
{
    assert(phead);
    //找尾
    LTNode* tail = phead->prev;
    //判空
    if(tail == phead);
        return;
    //找到尾结点的前一个结点(倒数第二个)
    LTNode* prevTail = tail->prev;
    //删除尾结点
    prevTail->next = phead;
    phead->prev = prevTail;
    free(tail);     
}
void LTPopFront(LTNode* phead)
{
    //同上
    assert(phead);
    LTNode* del = phead->next;
    if(del == phead)
        return;
    LTNode* next = del->next;
    next->prev = phead;
    phead->next = next;
    free(del);
}
//以下两个接口需要配合查找使用 所以删除接口不需要判断是否为头结点
void LTInsert(LTNode* pos, LTDataType x)
{
    assert(pos);
    LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
    assert(newnode);
    newnode->data = x;
    LTNode* prev = pos->prev;
    //插入
    pos->prev = newnode;
    newnode->next = pos;
    newnode->prev = prev;
    prev->next = newnode;
}
void LTErase(LTNode* pos)
{  
    assert(pos);
    LTNode* prev = pos->prev;
    LTNode* next = pos->next;
    prev->next = next;
    next->prev = prev;
    free(pos);
}
LTNode* LTFind(LTNode* phead, LTDataType x)
{
    assert(phead);
    LTNode* cur = phead;
    while(cur != phead)
    {
        if(cur->data == x)
            return cur;
        cur = cur->next;
    }
    return NULL;
}
void LTPrint(LTNode* phead)
{
    assert(phead);
    LTNode* cur = phead->next;
    printf("head");
    while(cur != phead)
    {
        printf("<-%d->", cur->data);
        cur = cur->next;
    }
    printf("head\n");
}
void ListDestroy(LTNode* phead)
{
	assert(phead);
	LTNode* cur = 	phead->next;
	while(cur != phead)
	{
		LTNode* next = cur->next;
		ListErase(cur);
		cur = next;
	}
	free(phead);
}
薛定谔 | 先导化合物优化与相互作用可视化 当获得针对某个靶点的先导分子后,想要在此基础上进行化学结构修饰。为了提升化学改造的成功率可以采用本教程方法进行结构虚拟改造/优化。每用薛定谔处理一个新任务,第一步必须创建新的project以方便日后管理。选项卡中修改工作路径(可以方便地找到输出文件) 阅读详情

相关推荐

LangChain开发避坑指南:为什么Pylance总提示‘无法解析导入‘?实测7种修复方案

本文深入解析了在VS Code中使用LangChain开发时,Pylance频繁提示‘无法解析导入’的根本原因——Python解释器与语言服务器的环境路径不一致。文章提供了从基础排查到深度配置的7种实测修复方案,包括切换解释器、配置extraPaths、使用pyrightconfig.json等,旨在帮助开发者彻底解决此问题,提升AI应用开发效率。

weixin_28826521的博客 447

C数据结构设计中,表首结点和二叉树根结点的头指针——传指针还是传指针的指针?

在链表中添加节点,为什么AddToTail(ListNode** pHead,int value)的第一个形参是指向节点指针的指针??         书上的解释如下:第一个参数pHead是一个指向指针的指针,当向一个空链表插入一个节点时,新插入的节点是链表的头指针,此时会改动头指针,因此必须把pHead参数设置为指向指针的指针。         总的来说这样做的目的是为了应对“空链表”的情况

AlphaGQ 1627

【车辆仿真】从0开始建立dSPACE RT Simulink工程:CAN通信(RTICANMM方法)

【工程环境信息】:本文基于链接文章搭建SCALEXIO Rack RTPC总线节点,用于与AutoBox通信硬件:上位机:惠普台式PC(Windows 11专业版)下位机(实时机):AutoBox (DS1006)板卡:DS6342软件:本文所使用的DBC链接提示:以下是本篇文章正文内容,下面案例可供参考!!!的部分一定记得配置,其他的按需配置RTICANMM的灵活度非常高,尽可能参考Help文档,理解相应的配置内容官方有RTICANMM的样例工程,在下方路径。

winnizap的博客 2381

C语言手撕实战代码_循环单链表和循环双链表

C语言手撕实战代码_循环单链表和循环双链表

weixin_62613321的博客 1156

Hermes深入理解及源码解析(二):Hermes的记忆机制

源码位置:tools/memory_tool.py存储内容为两个文件,放在 ~/.hermes/memories/:MEMORY.md:Agent 自己的笔记(环境事实、项目约定、工具坑、学到的东西)—— 上限 2200 字符USER.md:关于用户的画像(角色、偏好、沟通风格)—— 上限 1375 字符作为一个独立的工具注册在系统中(为了让智能体正确使用它,System Prompt()中明确下达了禁令存储目标存储位置数据类型持久偏好/事实MEMORY.md我是谁、我喜欢什么、环境配置。

qq_41984117的博客 561

axi时序图_(AXI)握手协议(pvld/prdy或者valid-ready)中ready打拍技巧

内容提要ready打拍的问题用FIFO的思路去解决用Buffer的思路去解决问题提出:ready时序如何优化?在valid/ready 握手协议中,valid 与 data的时序优化比较容易理解,(不熟悉valid/ready协议或者valid打拍方法的)大家可以参考上次推送(握手协议(pvld/prdy或者valid-ready或AXI)中Valid及data打拍技巧)。 但是有时候,关键路径是...

weixin_29763641的博客 3325

C语言 不带头结点的循环双链表的实现和相关操作。

C语言 不带头结点的循环双链表的实现和相关操作。 #include<stdio.h> #include<stdlib.h> typedef int DAT; typedef struct node { struct node *prior;//指向前一节点 DAT data;//储存数据 struct node *next;//指向下一节点 }Node; void init_list(Node **pL)//初始化链表 { (*pL)=NULL; } Node* ma

ok_阿伟的博客 1426

Diffusion:如何从噪声中生成清晰图像

扩散模型是当前AI图像生成领域的核心基础,如Stable Diffusion、DALL·E等热门模型均采用该技术。其原理分为训练和推理两个阶段:训练时通过逐步加噪图片,让AI学习如何去除噪声;推理时则从随机噪声出发,结合文本提示逐步去噪生成清晰图像。模型架构通常包含文本编码器、生成模型(如U-Net)和解码器三部分,通过多轮迭代将低维潜在空间表示转化为最终图像。这种技术模拟了艺术家从混沌中雕琢作品的过程,实现了从随机噪声到目标图像的智能生成。

laumy的学习笔记 1276

线性表练习之Example037-判断带头节点的循环双链表是否对称

请设计一个算法用于判断带头节点的循环双链表是否对称。

二木成林 2844

【SWAT水文模型】SWAT-CUP参数率定与模型验证实战指南

本文提供了SWAT水文模型参数率定与模型验证的完整实战指南。详细介绍了如何使用SWAT-CUP工具,特别是SUFI-2算法,进行自动化参数校准,并阐述了模型验证的关键步骤以避免过度拟合。内容涵盖从工程搭建、核心参数选择、迭代优化到结果分析与常见问题排查的全流程,旨在帮助用户提升SWAT模型模拟的准确性与可靠性。

weixin_29072299的博客 499

python3.12 在用django5.0创建超级用户时,遇到AttributeError: module ‘hashlib‘ has no attribute ‘pbkdf2_hmac‘的问题

产生问题的原因是python3.12的hashlib没有自带pbkdf2_hmac函数,该函数是加密算法的一种,只要通过别的函数导入pbkdf2_hmac即可。还有一种方法是将django默认使用的pbkdf2_hmac改为hashlib自带的sha2或其他算法返回加密的密码,笔者以后会尝试用此类办法解决问题。2.找到报错文件的位置/usr/local/lib/python3.12/site-packages/django/utils/crypto.py。保存修改的文件,重新执行创建超级用户的命令成功。

weixin_67057943的博客 852

数据结构||(C语言版)带头循环双链表

带头循环 DLOOP.h #include<stdio.h> #include<stdlib.h> #include<assert.h> #include<stdbool.h> typedef int DLDataType; typedef struct DListNode{ DLDataType _value; struct DListN...

Template Code 712

汽车行业OTD业务模式入门学习

本文是在学习彭俊松老师的《汽车行业整车订单交付系统》后,对于OTD理解的一些摘录,供诸位参考学习。

qq_40664711的博客 1万+

C语言实现非循环双链表节点的删除(带头结点尾结点)

我在之前一篇博客《C语言实现非循环双链表节点的删除(不带头结点)》中详细讲解了不含头尾节点的双链表中删除一个节点,处理过程还是稍显麻烦。自从我们学习使用头尾节点来处理双链表后,删除过程就非常方便。代码上传至 https://github.com/chenyufeng1991/DeleteNodeDoubleLinkedList_HeadList 。核心代码如下://删除pos位置的节点 int D

乞力马扎罗的雪CYF的博客 3991

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

C语言实现非循环双链表节点的删除(不带头结点)

不带头结点的非循环双链表在删除节点的时候比价麻烦,因为同时要维护prior和next两个指针。在处理第一个节点和最后一个节点的时候都要分别考虑,同时也需要考虑节点数量为1的情况。删除情况分为下面两类:(1)删除pos位置的节点;(2)判断x是否在链表中,若存在则删除;代码上传至 https://github.com/chenyufeng1991/DeleteNodeDoubleList  。核心代

乞力马扎罗的雪CYF的博客 3654

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

循环双链表(C语言)

今天也要努力学习,争取考上杭电。 链表的知识 链表即线性表的链式存储。 通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据间的线性关系,对每个链表结点,除了存放数据元素的信息外,还需要存放一个指向其后继的指针(双链表则还需要一个指向前驱的指针)。 链表解决了顺序表需要大量连续存储单元的缺点,但链表要附加指针域,也浪费了存储空间。存储密度不如顺序表大。 相比顺序表的随机访问,链表则是非随机...

GR_001的博客 417

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

(带头结点)循环双链表的创建于与输出

嗯,初学者第一次写博客

qq_54628272的博客 3139

C语言 带头双向循环链表的增删查改功能实现

这里要注意有个头结点,且有前后指针才可以实现循环,写功能时思路必须要清晰。 头文件 功能定义 // 带头+双向+循环链表增删查改实现 #pragma once #include <stdio.h> #include <stdlib.h> #include <assert.h> typedef int LTDataType; typedef struct L...

e我所欲也的博客 839

循环双链表的实现_2027考研408

本文介绍了循环双链表的数据结构实现,主要内容包括: 循环双链表的基本概念和结构特点:在双向链表基础上首尾相连形成环状结构,每个结点包含前驱和后继指针,无NULL指针。 循环双链表的实现细节: C语言结构体定义 空表的三种表示方法 初始化方法(带头结点) 基本操作实现: 按位查找(普通版和优化版) 按值查找 插入操作(前插和后插),重点讲解了后插操作的4个指针修改步骤 循环双链表的优势在于插入删除操作的高效性,已知结点时前插和后插都只需O(1)时间。 通过图解展示了后插操作的具体过程。

WODNT的博客 652

数据结构 || 带头非循环双链表

带头非循环双链表学习

Template Code 1789

带你玩转双链表

相信经过前面的学习,大家已经了解的单链表的缺陷和用途,今天我们学习双链表,和以前不同,今天双链表的实现我们增加一点点的难度,但我相信这些难度对大家都没有问题。和之前单链表的实现,我们的数据类型是固定的,主函数中传什么我们的单链表结构体中就需要相应的数据类型,今天双链表的实现我们将改为主函数(用户)传任何的数据类型我们都可以接收并且实现。本章涉及函数指针,回调函数,柔性数组的知识点,忘记的小伙伴们可以在本章复习一下哟。相信大家对我们双链表已经有了很深的了解,这个带头循环链表将是我们使用最多的结构。

2301_76986069的博客 1038

C语言 带有头结点的循环双链表的实现和相关操作。

C语言 带有头结点的循环双链表的实现和相关操作。 #include<stdio.h> #include<stdlib.h> typedef int DAT; typedef struct node { struct node *prior;//指向前一节点 DAT data;//存储数据 struct node *next;//指向下一节点 }Node; typedef struct { Node *head;//指向头节点 int size;//存储节点个数

ok_阿伟的博客 1365

构造带头结点的循环双链表并判断其是否对称(C语言实现)

#include<stdio.h> #include<stdlib.h> #define ElemType int typedef struct DNode{ ElemType data; struct DNode *next,*prior; }DNode,*DinkList; //构造一个带头结点的循环双链表 DinkList List_HeadInsert(DinkList &L) { DNode *s,*r; int x; L=(DinkList)mal

weixin_43638873的博客 511

C语言创建双链表用尾插法,C实现头插法和尾插法来构建非循环双链表(不带头结点)(示例代码)...

在实际使用中,双链表比单链表方便很多,也更为灵活。对于不带头结点的非循环双链表的基本操作,我在《C语言实现双向非循环链表(不带头结点)的基本操作》这篇文章中有详细的实现。今天我们就要用两种不同的方式头插法和尾插法来建立双链表。代码上传至 https://github.com/chenyufeng1991/HeadInsertAndTailInsertDoubleList 。核心代码如下://尾...

weixin_42356349的博客 412

考研数据结构——C语言实现循环双链表

C语言实现循环双链表

weixin_58879477的博客 558

c语言链表循环不进行,C语言实现非循环双链表节点的删除(不带头结点)

不带头结点的非循环双链表在删除节点的时候比价麻烦,因为同时要维护prior和next两个指针。在处理第一个节点和最后一个节点的时候都要分别考虑,同时也需要考虑节点数量为1的情况。删除情况分为下面两类:(1)删除pos位置的节点;(2)判断x是否在链表中,若存在则删除;核心代码如下://删除pos位置的节点Node *deletePosList(Node *pNode,int pos){//注意需要...

weixin_42509868的博客 268
上一篇: [C语言]文件操作
下一篇: [C语言实现]数据结构——手撕顺序栈之我出生就会写一个栈
FlashRider
博客等级 码龄4年 529粉丝 24原创
评论 11
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值