带头双向循环链表

  • 结构描述:

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以店会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

// 2、带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* _next;struct ListNode* _prev;
}ListNode;typedef struct Linklist
{struct ListNode* _head;
}Linklist;

带头双向循环链表中,链表结构包含一个头节点,但头节点不保存数据,第一个数据节点为head->next,同时每一个数据节点结构包含三部分,一部分为data数据,一部分为prev指向前一节点,另外一部分next指向后一节点,结构虽然复杂,但执行数据操作的过程中发现插入和删除操作的算法时间复杂度均为O(1)

  • 接口声明:

//创建新节点
ListNode* createNode(LTDataType x);
// 初始化.
ListNode* ListInit(Linklist* plist);
// 双向链表销毁
void ListDestory(Linklist* plist);
// 双向链表打印
void ListPrint(Linklist* plist);
// 双向链表尾插
void ListPushBack(Linklist* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(Linklist* plist);
// 双向链表头插
void ListPushFront(Linklist* plist, LTDataType x);
// 双向链表头删
void ListPopFront(Linklist* plist);
// 双向链表查找
ListNode* ListFind(Linklist* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

下面开始实现每一个接口,完成接口定义:其中接口实现可以按照如下的链表结构来理解其中指针的指向

(1)初始化:

// 创建返回链表的头结点.
ListNode* ListInit(Linklist* plist)
{plist->_head = (struct ListNode*)malloc(sizeof(struct ListNode));plist->_head->_prev = plist->_head->_next = plist->_head;return plist->_head;
}

(2)创建新节点

//创建新节点
ListNode* createNode(LTDataType x)
{struct ListNode* newnode= (struct ListNode*)malloc(sizeof(struct ListNode));newnode->_data = x;newnode->_next = newnode->_prev = NULL;return newnode;
}

(3)尾插:

根据上述示例结构,在尾部插入一个新节点

代码如下:更改四个指针指向即可完成

//尾插
void ListPushBack(Linklist* plist, LTDataType x)
{struct ListNode* newnode = createNode(x);struct ListNode* tail = plist->_head->_prev;tail->_next = newnode;newnode->_prev = tail;plist->_head->_prev = newnode;newnode->_next = plist->_head;//ListInsert(plist->_head, x);//头前插入 即尾插
}

考虑只有头结点情况

发现以上代码对于只包含头结点的空链表依然适用,尾插不需要对只包含头结点的空链表特殊处理

(4)尾删

根据head->_prev找到尾结点  从而记录尾结点前一节点  释放尾结点,更改四个指针指向

// 双向链表尾删
void ListPopBack(Linklist* plist)
{//空链表不能删除,破坏双向带头循环链表结构if (plist->_head == plist->_head->_next)return;struct ListNode* tail = plist->_head->_prev;struct ListNode* prev = tail->_prev;free(tail);prev->_next = plist->_head;plist->_head->_prev = prev;//ListErase(plist->_head->_prev);
}

考虑只有头结点 是否可以删除尾结点操作

删除后会破坏了双向带头循环链表的结构,所以仅包含头结点的双向带头循环链表无法完成尾删操作

(5)头插

//头插
void ListPushFront(Linklist* plist, LTDataType x)
{struct ListNode* newnode = createNode(x);struct ListNode* next = plist->_head->_next;plist->_head->_next = newnode;newnode->_prev = plist->_head;newnode->_next = next;next->_prev = newnode;//ListInsert(plist->_head->_next, x);//头的下一个插入 ,即头插
}

在头插的过程中,无论是否只有头结点,处理都是一样的  只不过只包含头结点的情况下,head->_next=head->_prev=head

(6)头删

头删是删除第一个数据节点,如果空链表  无法删除,否则破坏链表结构

// 双向链表头删
void ListPopFront(Linklist* plist)
{if (plist->_head == plist->_head->_next)return;struct ListNode* next = plist->_head->_next;struct ListNode* nextnext = next->_next;free(next);nextnext->_prev = plist->_head;plist->_head->_next = nextnext;//ListErase(plist->_head->_next);}

(7)任意位置插入/删除

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{struct ListNode* newnode = createNode(x);struct ListNode* prev = pos->_prev;pos->_prev = newnode;newnode->_next = pos;prev->_next = newnode;newnode->_prev = prev;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{if (pos->_next == pos->_prev)return;struct ListNode* next = pos->_next;struct ListNode* prev = pos->_prev;free(pos);next->_prev = prev;prev->_next = next;
}

(8)遍历链表打印

void ListPrint(Linklist* plist)
{struct ListNode* cur = plist->_head->_next;while (cur != plist->_head){printf("%d ", cur->_data);cur = cur->_next;}printf("\n");
}

(9)链表销毁

//销毁链表
void ListDestory(Linklist* plist)
{struct ListNode* cur = plist->_head->_next;//此处从第一个数据节点开始while (cur != plist->_head){struct ListNode* next = cur->_next;free(cur);cur = next;}free(plist->_head);//头结点作为特例删除
}

(10)测试代码:

#include<stdio.h>#include"linklist.h"void test()
{struct Linklist plist;ListInit(&plist);printf("尾插:\n");ListPushBack(&plist,1);ListPushBack(&plist, 2);ListPushBack(&plist, 3);ListPushBack(&plist, 4);ListPushBack(&plist, 5);ListPrint(&plist);printf("尾删:\n");ListPopBack(&plist);ListPrint(&plist);ListPopBack(&plist);ListPrint(&plist);ListPopBack(&plist);ListPrint(&plist);ListPopBack(&plist);ListPrint(&plist);ListPopBack(&plist);ListPrint(&plist);printf("头插:\n");ListPushFront(&plist, 1);ListPushFront(&plist, 2);ListPushFront(&plist, 3);ListPushFront(&plist, 4);ListPushFront(&plist, 5);ListPrint(&plist);printf("头删:\n");ListPopFront(&plist);ListPrint(&plist);ListPopFront(&plist);ListPrint(&plist);ListPopFront(&plist);ListPrint(&plist);ListPopFront(&plist);ListPrint(&plist);ListPopFront(&plist);ListPrint(&plist);
}int main()
{test();
}

执行结果如下:

C语言实现链表【二】带头双向循环链表相关推荐

  1. 【数据结构】链表:带头双向循环链表的增删查改

    本篇要分享的内容是带头双向链表,以下为本片目录 目录 一.链表的所有结构 二.带头双向链表 2.1尾部插入 2.2哨兵位的初始化 2.3头部插入 2.4 打印链表 2.5尾部删除 2.6头部删除 2. ...

  2. 【数据结构】带头双向循环链表的增删查改(C语言实现)

    文章目录 前言 一.什么是带头双向循环链表 二.带头双向循环链表的实现 1.结构的定义 2.链表的初始化 3.开辟新节点 4.在头部插入数据 5.在尾部插入数据 6.查找数据 7.在pos位置之前插入 ...

  3. c语言实现数据结构中的带头双向循环链表

    目录标题 一.单向链表的不足 二.带头双向链表的准备 三.带头双向链表的初始化 四.带头双向链表的尾插 五.带头双向链表的打印 六.带头双向链表头插 七.判断链表是否为空 八.带头双向链表尾删 九.带 ...

  4. 【数据结构初阶】链表(下)——带头双向循环链表的实现

    目录 带头双向循环链表的实现 1.带头双向循环链表的节点类型 2.创建带头双向循环链表的节点 3.向带头双向循环链表中插入数据 <3.1>从链表尾部插入数据 <3.2>从链表头 ...

  5. 带头+双向+循环链表(C语言)

    文章目录 什么是带头+双向+循环链表 节点的创建 链表的初始化 打印函数 查找函数 尾插函数 尾删函数 头插函数 头删函数 在pos位置前插入 任意位置删除 销毁函数 什么是带头+双向+循环链表 带头 ...

  6. 比特数据结构与算法(第二章收尾)带头双向循环链表的实现

    1.链表的分类 链表的分类 ① 单向或者双向 ② 带头或者不带头 ③ 循环或者非循环 常用的链表: 根据上面的分类我们可以细分出8种不同类型的链表,这么多链表我们一个个讲解这并没有意义.我们实际中最常 ...

  7. 初阶数据结构之带头+双向+循环链表增删查实现(三)

    文章目录 @[TOC](文章目录) 前言 一.带头双向循环链表的初始化 1.1带头双向循环链表的结构体定义 1.2初始化代码的实现 二.带头+双向+循环链表的增功能实现 2.1头插代码的实现 2.2尾 ...

  8. 数据结构:带头双向循环链表——增加、删除、查找、修改,详细解析

    读者可以先阅读这一篇:数据结构--单链表的增加.删除.查找.修改,详细解析_昵称就是昵称吧的博客-CSDN博客,可以更好的理解带头双向循环链表. 目录 一.带头双向循环链表的处理和介绍 1.带头双向循 ...

  9. 【数据结构】-关于带头双向循环链表的增删查改

    作者:低调 作者宣言:写好每一篇博客 文章目录 前言 一.带头双向循环链表的实现 1.1创建返回链表的头结点 1.2开辟一个新的结点 1.3双向链表的销毁 1.4双向链表的打印 1.5双向链表尾插 1 ...

最新文章

  1. python2 urllib 笔记
  2. HDU1160:FatMouse's Speed(最长上升子序列,不错的题)
  3. python人工智能——机器学习——分类算法-k近邻算法
  4. 【渝粤教育】广东开放大学 知识产权法 形成性考核 (34)
  5. C#正则表达式判断输入日期格式是否正确
  6. no connection could be made because the target machine actively refused it.问题解决
  7. python __builtins__ copyright类 (14)
  8. java中数字循环嵌套举例,在Java程序中,复杂算法可以通过循环语句和的相互嵌套来实现。...
  9. Python 爬虫 书籍爬取实例
  10. 【2022修复版】社群扫码进群活码引流完整运营源码/对接免签约支付接口/推广正常绑定下级/带视频搭建教程
  11. FBW7通过端粒脱帽介导肺纤维化和衰老
  12. 使用fiddler自动化抓取微信公众号文章的点赞与阅读数
  13. mwt是什么意思网络用语_1,2,3,4,5,6,7,8,9,0的网络语言代表什么意思啊?
  14. CMA实验室盲样考核,如何控制质量?
  15. Android(安卓)是什么?
  16. 日海通讯2016Q1营收4.95亿元 同比下降12%
  17. 高性能数据库集群方案
  18. MySQl之最全且必会的sql语句
  19. 司马懿PPT学校免费课程合集(免费赠送)
  20. MyEclipse打开JSP文件报Failed to create the part's controls解决方法汇总

热门文章

  1. 如何在1微秒内检测数以万计的ip段
  2. 2020 CCPC Changchun F :Strange Memory dsu on tree
  3. CreCloud云网管管理平台操作流程
  4. 智慧安防智能化发展趋势及解决方案
  5. 海康摄像头web集成播放,ffmpeg+nginx方案
  6. 如何在计算机写作业英语作文,关于抄作业的英语作文4篇
  7. 小学计算机教案范文,小学三年级计算机教案范文
  8. 2019年秋季计算机应用基础,天大2019年秋季考试《计算机应用基础》在线考核试题【满分答案】...
  9. Java 8 Update 31 (8u31)
  10. ARMv8架构u-boot启动流程详细分析(一)