数据结构入门(C语言版)线性表带头双向循环链表接口实现(上)

简介: 第一步当然是先使用malloc函数申请内存空间,然后就是两个指针的建立,尾部指针指向头结点头部,头部指针指向头结点尾部,返回带头结点,头结点初始化完成。

5d74882d88c44e34904e221ddbc59cb0.png


导航


1、带头双向循环链表介绍


52c51e96575442859756ce7e7b6e8343.jpg


在上一篇博客我们讲述了链表的概念和结构,还实现了无头单向非循环链表接口写法,那么这一章节,我们来实现另一种常用的链表组成结构——带头双向循环链表。

如果对前面的链表基本概念还是不了解,可以看作者的上一篇博客:线性表中链表介绍及无头单向非循环链表接口实现


2、结构体及接口函数定义


首先是结构体的定义

代码如下:


typedef int LTDateType;
typedef struct ListNode
{
  LTDateType data;//结点存储元素
  struct ListNode* next;//下一结点指针
  struct ListNode* prev;//上一结点指针
}LTNode;


然后就是接口函数的定义

代码如下:


void ListInit(LTNode* phead);//哨兵位头结点初始化
LTNode* BuyListNode(LTDateType x);//动态申请结点
void ListPushBack(LTNode* phead, LTDateType x);//双向链表尾插
void ListPopBack(LTNode* phead);//双向链表尾删
void ListPushFront(LTNode* phead, LTDateType x);//双向链表头插
void ListPopFront(LTNode* phead);//双向链表头删
LTNode* ListFind(LTNode* phead, LTDateType x);//双向链表查找
void ListInsert(LTNode* pos, LTDateType x);//在pos位置前插入
void ListErase(LTNode* pos);//删除pos位置的结点
void ListPrint(LTNode* phead);//打印双向链表
void ListDestroy(LTNode* phead);//销毁双向链表


3、接口函数实现


在上一篇博客中我们讲到不带头的单非循环链表存在一定缺陷,就是无法访问上一结点,但是这一节讲的带头双向循环链表就很好的弥补了这一缺点,带头双向循环链表看来比单链表结构要复杂很多,但其实实现起来要比单链表更简单,更高效;下面我们就来实现带头双向循环链表的接口函数吧!


3.1 头结点初始化


头结点初始化(ListInit)

首先是我们的头结点的定义,它是不存放数据的,起到一个哨兵的作用

代码如下:


void ListInit(LTNode* phead)
{
  // 哨兵位头结点
  LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
  phead->next = phead;
  phead->prev = phead;
  return phead;
}


第一步当然是先使用malloc函数申请内存空间,然后就是两个指针的建立,尾部指针指向头结点头部,头部指针指向头结点尾部,返回带头结点,头结点初始化完成。


3.2 结点动态内存申请


结点动态内存申请(BuyListNode)

这个函数和上一篇中的单链表的函数类似

代码如下:


LTNode* BuyListNode(LTDateType x)
{
  LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
  newnode->data = x;
  newnode->next = NULL;
  newnode->prev = NULL;
  return newnode;
}


第一步也是先使用malloc函数申请内存空间,然后就是初始化这个结点的操作,将元素插入,两个指针指向空,返回新结点,完成结点初始化操作。


3.3 双向链表尾插


双向链表尾插(ListPushBack)

代码如下:


void ListPushBack(LTNode* phead, LTDateType x)
{
  assert(phead);
  LTNode* tail = phead->prev;
  LTNode* newnode = BuyListNode(x);
  tail->next = newnode;
  newnode->prev = tail;
  newnode->next = phead;
  phead->prev = newnode;
}


这里我们先是把phead的上一级指针赋给tail

再将要插入的元素赋给临时结点newnode

接着将tail的下一级指针指向newnode

再将newnode上一级指针指向tail

newnode下一级指针指向被插入结点phead

最后将phead的上一级指针再指向newnode完成尾插操作


3.4 双向链表尾删


双向链表尾删(ListPopBack)

代码如下:


void ListPopBack(LTNode* phead)
{
  assert(phead);
  assert(phead->next != phead);//防止链表中无元素继续删除的断言
  LTNode* tail = phead->prev;
  phead->prev = tail->prev;
  tail->prev->next = phead;
  free(tail);
}


上述代码中第二个断言是为了防止链表中无元素继续删除

这里我们也是先把phead的上一级指针赋给tail

再将tail的上一级指针赋给phead的上一级指针

也就是指向要删除结点的上一结点

最后将要删除结点的前一个结点的下一级指针指向头结点

然后释放掉tail的内存空间完成尾删


3.5 双向链表头插


双向链表头插(ListPushFront)

代码如下:


void ListPushFront(LTNode* phead, LTDateType x)
{
  assert(phead);
  LTNode* newnode = BuyListNode(x);
  LTNode* next = phead->next;
  phead->next = newnode;
  newnode->prev = phead;
  newnode->next = next;
  next->prev = newnode;
}


这里我们先和尾插一样将要插入的元素值赋给临时结点newnode

将phead下一级指针赋给临时结点next

再将newnode赋给phead下一级指针

也就是把phead的尾部指针指向newnode

把phead赋给newnode的上一级指针

再将next赋给newnode的下一级指针

最后把newnode赋给next的上一级指针,完成头插

相关文章
|
22天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
22天前
|
C语言
数据结构基础详解(C语言):图的基本概念_无向图_有向图_子图_生成树_生成森林_完全图
本文介绍了图的基本概念,包括图的定义、无向图与有向图、简单图与多重图等,并解释了顶点度、路径、连通性等相关术语。此外还讨论了子图、生成树、带权图及几种特殊形态的图,如完全图和树等。通过这些概念,读者可以更好地理解图论的基础知识。
|
24天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
24天前
|
存储 算法 C语言
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
|
24天前
|
存储 C语言 C++
数据结构基础详解(C语言) 顺序表:顺序表静态分配和动态分配增删改查基本操作的基本介绍及c语言代码实现
本文介绍了顺序表的定义及其在C/C++中的实现方法。顺序表通过连续存储空间实现线性表,使逻辑上相邻的元素在物理位置上也相邻。文章详细描述了静态分配与动态分配两种方式下的顺序表定义、初始化、插入、删除、查找等基本操作,并提供了具体代码示例。静态分配方式下顺序表的长度固定,而动态分配则可根据需求调整大小。此外,还总结了顺序表的优点,如随机访问效率高、存储密度大,以及缺点,如扩展不便和插入删除操作成本高等特点。
|
24天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
24天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
147 3
|
24天前
|
存储 C语言
C语言程序设计核心详解 第九章 结构体与链表概要详解
本文档详细介绍了C语言中的结构体与链表。首先,讲解了结构体的定义、初始化及使用方法,并演示了如何通过不同方式定义结构体变量。接着,介绍了指向结构体的指针及其应用,包括结构体变量和结构体数组的指针操作。随后,概述了链表的概念与定义,解释了链表的基本操作如动态分配、插入和删除。最后,简述了共用体类型及其变量定义与引用方法。通过本文档,读者可以全面了解结构体与链表的基础知识及实际应用技巧。
|
1月前
|
存储 测试技术 C语言
C语言实现链表的各种功能
本文详细介绍了如何使用C语言实现链表的各种功能,包括链表节点结构的定义与操作函数的实现。链表作为一种常用的数据结构,具有节点自由插入删除、动态变化等特点。文中通过`link_list.h`和`link_list.c`两个文件,实现了链表的初始化、插入、删除、查找、修改等核心功能,并在`main.c`中进行了功能测试。这些代码不仅展示了链表的基本操作,还提供了丰富的注释帮助理解,适合作为学习链表的入门资料。
|
24天前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。