C语言实现带头双向循环链表

文章目录

  • 写在前面
  • 1. 链表节点的定义
  • 2. 链表的初始化
  • 3. 插入数据
    • 3.1 头插
    • 3.2 尾插
    • 3.3 在指定位置的前面插入数据
  • 4 删除数据
    • 4.1 头删
    • 4.2 尾删
    • 4.3 删除指定位置的数据
  • 5 查找并修改数据
  • 5. 链表的销毁

写在前面

上面文章用C语言实现了单链表的增删查改,我们知道,单链表只能从头结点开始正向遍历,而在单链表中插入或删除节点时,需要修改前一个节点的指针,因此在单链表中插入或删除节点时需要遍历链表找到前一个节点,导致插入和删除操作的效率较低。为了能够高效率解决类似的问题,本片文章继续用C语言来实现另一种线性存储结构——带头双向循环链表。
我们从它的逻辑结构来更深层次的理解一下带头双向循环链表:
在这里插入图片描述

1. 链表节点的定义

链表的结点分为三部分:指针域、数据域、指针域
指针域:用于指向当前节点的直接前驱节点
数据域:链表要存储的数据所在的区域。
指针域:用于指向当前节点的直接后继节点。

链表节点的逻辑图:
在这里插入图片描述
链表节点的定义:

typedef int STDataType;
typedef struct ListNode
{struct ListNode* prev;//指针域, 指向上一个节点struct ListNode* next;//指针域, 指向下一个节点STDataType val;//数据域
}LTNode;

2. 链表的初始化

该链表在初始化的时候,只需要创建哨兵位的头节点即可,并将该节点的地址返回。该节点不存储有效数据,其prev 和 next指针指向自己。
在这里插入图片描述
由于后面的插入都需要创建新的节点,因此这里把创建节点封装成一个函数。

LTNode* BuyNode(LTDataType x)
{LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));if (newnode == NULL){perror(" BuyNode()");}newnode->val = x;newnode->prev = NULL;newnode->next = NULL;return newnode;
}

链表的初始化代码如下:

LTNode* LTInit()
{//创建哨兵位的头节点LTNode* newnode = BuyNode(-1);//prev 和 next指针指向自己newnode->next = newnode;newnode->prev = newnode;return newnode;
}

3. 插入数据

向链表插入数据时,根据插入位置的不同可以分为以下三种情况:

  • 在头节点前插入一个元素,即头插。
  • 在链表中间位置插入元素。
  • 在最后一个节点后面插入一个元素,即尾插。

3.1 头插

头插数据步骤:

  1. 首先,创建一个新的节点,并用 val 初始化其数据域。
  2. 将新节点插入到链表的头部,更新指针。
    在这里插入图片描述
    代码如下:
void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);//检查参数有效性LTNode* newnode = BuyNode(x);//创建新节点LTNode* next = phead->next;//修改指针链接关系newnode->next = next;next->prev = newnode;newnode->prev = phead;phead->next = newnode;
}

3.2 尾插

尾插数据步骤:

  1. 首先,创建一个新的节点,并用 val 初始化其数据域。
  2. 将新节点插入到链表的尾部,更新指针。

在这里插入图片描述
代码如下:

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);//检查参数有效性LTNode* newnode = BuyNode(x);//创建新节点LTNode* tail = phead->prev;//修改指针链接关系newnode->prev = tail;tail->next = newnode;newnode->next = phead;phead->prev = newnode;//LTInsert(phead, x);
}

3.3 在指定位置的前面插入数据

  1. 首先,创建一个新的节点,并用 val 初始化其数据域。
  2. 将新节点插入到链表的 pos 位置之前,更新指针。
    在这里插入图片描述

代码如下:

void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);//检查位置有效性LTNode* newnode = BuyNode(x);//创建新节点LTNode* posPrev = pos->prev;//修改指针链接关系pos->prev = newnode;newnode->next = pos;posPrev->next = newnode;newnode->prev = posPrev;
}

4 删除数据

4.1 头删

头删的步骤如下:

  1. 判断链表是否为空,不为空在进行删除。
    判断链表是否为空的代码如下:
bool LTEmpty(LTNode* phead)
{return phead->next == phead;
}
  1. 删除第一个节点,并更新指针。

在这里插入图片描述

代码如下:

void LTPopFront(LTNode* phead)
{assert(phead);//检查参数有效性assert(!LTEmpty(phead));//判断链表是否为空LTNode* pos = phead->next;LTNode* posNext = pos->next;free(pos);//删除第一个节点修改指针链接关系phead->next = posNext;posNext->prev = phead;
}

4.2 尾删

头删的步骤如下:

  1. 判断链表是否为空,不为空在进行删除。
  2. 删除最后一个节点,并更新指针。
    在这里插入图片描述
    代码如下:
void LTPopBack(LTNode* phead)
{assert(phead);//检查参数有效性assert(!LTEmpty(phead));//判断链表是否为空LTNode* tail = phead->prev;LTNode* tailPrev = tail->prev;free(tail);//删除最后一个节点修改指针链接关系phead->prev = tailPrev;tailPrev->next = phead;
}

4.3 删除指定位置的数据

注意:删除指定位置的数据,需要传递正确的节点的地址,否则删除的结果是不确定的。

在这里插入图片描述
代码如下:

void LTErase(LTNode* pos)
{LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;free(pos);//删除指定位置的节点//修改指针链接关系posPrev->next = posNext;posNext->prev = posPrev;
}

5 查找并修改数据

遍历链表若找到目标节点,就返回目标节点的地址,否则返回空指针(NULL)。
该函数兼并修改的功能,因为该函数返回的是目标节点的地址。
代码如下:

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);//检查参数有效性LTNode* cur = phead->next;//遍历链表while (cur != phead){//找到返回,目标节点地址if (cur->val == x){return cur;}cur = cur->next;}//未找到,返回NLLreturn NULL;
}

5. 链表的销毁

  1. 依次释放链表的每个节点。
  2. 释放哨兵位的头节点。

注意:链表销毁以后,要在函数外面将头指针置空(NULL),以免造成野指针的问题。

代码如下:

void LTDestroy(LTNode* phead)
{assert(phead);//检查参数有效性LTNode* cur = phead->next;//依次释放链表的每个节点while (cur != phead){LTNode* next = cur->next;free(cur);cur = next;}free(phead);//释放哨兵位的头节点
}

至此,本片文章就结束了,若本篇内容对您有所帮助,请三连点赞,关注,收藏支持下。
创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !!!
在这里插入图片描述

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

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

相关文章

安顿APP3.0全新升级,引领智能穿戴健康革新,专注预警疾病风险

随着人们生活水平的提高和工作压力的增加,心脑血管疾病已经成为现代社会的严重问题,特别是心梗、脑卒中等疾病已经开始夺去年轻人的生命。 据报道,近年来,多位年轻人因心脑血管疾病突发去世,如42岁的知名男演员、30岁的…

oncoPredict文献学习

原文:oncoPredict: an R package for predicting in vivo or cancer patient drug response and biomarkers from cell line screening data oncoPredict:一种R包,用于从细胞系筛选数据中预测体内或癌症患者的药物反应和生物标志物 - PMC (n…

第十九章Java绘图

19.1Java绘图类 绘图是高级程序设计中非常重要的技术。 19.1.1Graphics类 Graphics类是所有图形上下文的抽象基类,它允许应用程序在组件以及闭屏图像上进行绘制 19.1.2Graphics2D类 使用Graphics2D类可以完成简单的图形绘制任务,但是她所实现的功能…

PC业务校验(已有该名称,已有该编码)

rules: {name: [{ required: true, message: "部门名称不能为空", trigger: "blur" },{min: 2,max: 10,message: "部门名称的长度为2-10个字符",trigger: "blur",},{trigger: "blur",validator: async (rule, value, callba…

图像倾斜角度求取-Radon变换

Radon算法 Radon(拉东)算法是一种通过定方向投影叠加,找到最大投影值时角度,从而确定图像倾斜角度的算法。具体过程如图所示 图1 Radon变换算法 Radon计算示例 对于纹理方向明显的图像,如图2所示,可以通…

5.Java中的注释及Javadoc文档

本文讲解 Java 中的注释以及 Javadoc 文档 ~ 文章目录 1. 注释1.1 引言1.1.1 何为注释?1.1.2 注释有何用?1.1.2.1 方便阅读1.1.2.2 调试程序 1.1.3 单行注释和多行注释 1.2 方法注释1.2.1 什么是方法注释?1.2.2 如何写方法注释?1.…

C/C++通过位操作实现2个uint32_t合并为uint64_t

#include <iostream> using namespace std;int main() {uint32_t a 10;uint32_t b 600;//先将uint32_t的a转为uint64_t&#xff0c;此时a前面32位都是0&#xff0c;然后左移32位&#xff0c;此时右32位为0&#xff0c;最后加上uint32_t类型的b&#xff0c;填充右32位的…

打开游戏提示xapofx1_5.dll丢失如何修复?xapofx1_5.dll缺失的修复教程分享

xapofx1_5.dll是一个重要的Windows系统文件&#xff0c;它主要负责处理图形渲染和多媒体功能。如果在计算机中找不到xapofx1_5.dll&#xff0c;可能会导致程序无法正常运行。下面是关于xapofx1_5.dll丢失的4个修复方法以及xapofx1_5.dll的作用和丢失原因的介绍。 一、xapofx1_…

Java 算法篇-链表的经典算法:有序链表去重、合并多个有序链表

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 链表的说明 2.0 有序链表去重的实现方式 2.1 有序链表去重(保留重复的节点) - 使用递归来实现 2.2 有序链表去重(保留重复的节点) - 使用双指针来实现 2.3 有序…

在线代码调试运行微信开放平台官方接口调试校验工具大全

具体前往&#xff1a;在线代码调试&API校验工具大全

Bulk RNA-seq上下游分析

Bulk-RNA-seq上下游分析还是相对简单的&#xff0c;这次我以mouse为例&#xff0c;进行Bulk-RNA-seq上下游分析&#xff0c;并进行对应的图片绘制。 上游分析 1.软件准备 #安装所需软件 sudo apt install fastqc sudo apt install hisat2 sudo apt install cutadapt sudo ap…

场景交互与场景漫游-交运算与对象选取(8-1)

交运算与对象选取 在面对大规模的场景管理时&#xff0c;场景图形的交运算和图形对象的拾取变成了一项基本工作。OSG作为一个场景管理系统&#xff0c;自然也实现了场景图形的交运算&#xff0c;交运算主要封装在osgUtil 工具中在OSG中&#xff0c;osgUtil是一个非常强有力的工…