数据结构入门(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的上一级指针,完成头插

相关文章
|
26天前
|
算法 数据处理 C语言
C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合
本文深入解析了C语言中的位运算技巧,涵盖基本概念、应用场景、实用技巧及示例代码,并讨论了位运算的性能优势及其与其他数据结构和算法的结合,旨在帮助读者掌握这一高效的数据处理方法。
38 1
|
1月前
|
存储 算法 搜索推荐
【趣学C语言和数据结构100例】91-95
本文涵盖多个经典算法问题的C语言实现,包括堆排序、归并排序、从长整型变量中提取偶数位数、工人信息排序及无向图是否为树的判断。通过这些问题,读者可以深入了解排序算法、数据处理方法和图论基础知识,提升编程能力和算法理解。
46 4
|
1月前
|
存储 机器学习/深度学习 搜索推荐
【趣学C语言和数据结构100例】86-90
本文介绍并用C语言实现了五种经典排序算法:直接插入排序、折半插入排序、冒泡排序、快速排序和简单选择排序。每种算法都有其特点和适用场景,如直接插入排序适合小规模或基本有序的数据,快速排序则适用于大规模数据集,具有较高的效率。通过学习这些算法,读者可以加深对数据结构和算法设计的理解,提升解决实际问题的能力。
43 4
|
24天前
|
存储 NoSQL 编译器
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
指针是一个变量,它存储另一个变量的内存地址。换句话说,指针“指向”存储在内存中的某个数据。
77 3
【C语言】指针的神秘探险:从入门到精通的奇幻之旅 !
|
24天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
244 6
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
6月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
6月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表
|
6月前
|
存储 算法 Java
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
66 2

热门文章

最新文章