文章目录

  • 一.带头双向循环链表
  • 二.实现
    • (1).动态申请一个结点
    • (2).创建头结点进行初始化
    • (3).尾插
    • (4).尾删
    • (5).头插
    • (6).头删
    • (7).查找元素
    • (8).在pos位置之前进行插入
    • (9).删除pos位置的结点
    • (10).打印数据
  • 三.代码实现

一.带头双向循环链表

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

二.实现

Lish.h中部分声明

typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;struct ListNode* next;LTDataType data;
}ListNode;

prev为结点中的指向前一个结点的指针 , next 为指针中的指向后一个结点的指针 , data为结点中的数据

(1).动态申请一个结点

动态申请一个大小为sizeof(ListNode)的结点

// 动态申请一个结点
ListNode*BuyListNode(LTDataType x)
{ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if(newNode == NULL){printf("分配内存失败\n");exit(-1);}newNode->prev = newNode->next = NULL;newNode->data = x;return newNode;
}

(2).创建头结点进行初始化

因为为带头的双向循环链表,因此初始化头结点时,头结点的prev,next指针都指向自已

// 创建头结点进行初始化
ListNode* ListInit()
{ListNode* plist = BuyListNode(0); // 头结点中的数据初始化为 0plist->prev = plist->next = plist;return plist;
}

(3).尾插

通过头结点的prev指针可以找到尾结点tail,tail->next指向待插入的结点,待插入结点的prev指针指向tail,头结点的prev指针指向待插入结点,待插入结点的next指向头结点

// 尾插
void ListPushBack(ListNode* plist,LTDataType x)
{assert(plist);ListNode* newNode = BuyListNode(x);ListNode* tail = plist->prev;tail->next = newNode;newNode->prev = tail;newNode->next = plist;plist->prev = newNode;
}

(4).尾删

通过头结点的prev指针找到尾结点tail,通过tail的prev指针找到 tailPrev结点

要使tailPrev成为尾结点,将tailPrev结点和plist头结点连接到一起

// 尾删
void ListPopBack(ListNode* plist)
{assert(plist);assert(plist->next != plist);ListNode* tail = plist->prev;ListNode* tailPrev = tail->prev;tailPrev->next = plist;plsit->prev = tailPrev;free(tail);tail = NULL;
}

(5).头插

动态申请一个结点newNode,通过plist->next找到当前的第一个结点(first),newNode结点要插入到plist头结点和first结点之间

// 头插
void ListPushFront(ListNode* plist,LTDataType x)
{assert(plist);ListNode* newNode = BuyListNode(x);ListNode* first = plist->next;plist->next = newNode;newNode->prev = plist;newNode->next = first;first->prev = newNode;
}

(6).头删

通过plist->next找到第一个结点(first),first->next找到第二个结点(second),将plist头结点和second结点连接到一起,释放掉第一个结点(first)

// 头删
void ListPopFront(ListNode* plist)
{assert(plist);assert(plist->next != plist);ListNode* first = plist->next;ListNode* second = first->next;plist->next = second;second->prev = plist;free(first);first = NULL;
}

(7).查找元素

遍历一次链表,终止条件为 cur != plist ,因为 cur = plist 说明已经循环了一遍

// 查找
ListNode* ListFind(ListNode* plist, LTDataType x)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}

(8).在pos位置之前进行插入

动态申请一个结点 ,找到pos位置的前一个结点(posPrev),将pos,posPrev,newNode结点连接到一起

// 在pos位置前插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* newNode = BuyListNode(x);ListNode* posPrev = pos->prev;posPrev->next = newNode;newNode->prev = posPrev;newNode->next = pos;pos->prev = newNode;
}

(9).删除pos位置的结点

找到pos的前一个结点(posPrev)和后一个结点(posNext),将posPrev,posNext结点连接到一起,删除掉pos结点

// 删除pos位置的结点
void ListErase(ListNode* pos)
{assert(pos);ListNode* PosPrev = pos->prev;ListNode* PosNext = pos->next;PosPrev->next = PosNext;PosNext->prev = PosPrev;free(pos);pos = NULL;
}

(10).打印数据

遍历一次链表,终止条件为 cur != plist ,因为 cur = plist 说明已经循环了一遍

// 打印
void Listprint(ListNode* plist)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}

三.代码实现

List.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDataType;
typedef struct ListNode
{struct ListNode* prev;struct ListNode* next;LTDataType data;
}ListNode;
// 初始化创建头结点
ListNode* ListInit();
// 动态申请一个结点
ListNode* BuyListNode(LTDataType x);
// 打印
void Listprint(ListNode* plist);
// 尾插
void ListPushBack(ListNode* plist, LTDataType x);
// 尾删
void ListPopBack(ListNode* plist);
// 头插
void ListPushFront(ListNode* plist, LTDataType x);
// 头删
void ListPopFront(ListNode* plist);
// 查找
ListNode* ListFind(ListNode* plist, LTDataType x);
// 在pos前插入
void ListInsert(ListNode* pos, LTDataType x);
// 删除pos位置的结点
void ListErase(ListNode* pos);

Lish.c文件

#include"List.h"
// 初始化创建头结点
ListNode* ListInit()
{ListNode* phead = BuyListNode(0);phead->next = phead;phead->prev = phead;return phead;
}
// 动态申请一个结点
ListNode* BuyListNode(LTDataType x)
{ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode));if (NewNode == NULL){printf("分配内存失败\n");exit(-1);}NewNode->prev = NewNode->next = NULL;NewNode->data = x;return NewNode;
}
// 打印
void Listprint(ListNode* plist)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){printf("%d ", cur->data);cur = cur->next;}printf("\n");
}
// 尾插
void ListPushBack(ListNode* plist, LTDataType x)
{assert(plist);ListNode* NewNode = BuyListNode(x);ListNode* tail = plist->prev;tail->next = NewNode;NewNode->prev = tail;plist->prev = NewNode;NewNode->next = plist;
}
// 尾删
void ListPopBack(ListNode* plist)
{assert(plist);assert(plist->next != plist);ListNode* tail = plist->prev;ListNode* tail_prev = tail->prev;tail_prev->next = plist;plist->prev = tail_prev;free(tail);tail = NULL;
}
// 头插
void ListPushFront(ListNode* plist, LTDataType x)
{assert(plist);ListNode* NewNode = BuyListNode(x);ListNode* first = plist->next;plist->next = NewNode;NewNode->prev = plist;NewNode->next = first;first->prev = NewNode;
}
// 头删
void ListPopFront(ListNode* plist)
{assert(plist);assert(plist->next != plist);ListNode* first = plist->next;ListNode* second = first->next;plist->next = second;second->prev = plist;free(first);first = NULL;
}
// 查找
ListNode* ListFind(ListNode* plist, LTDataType x)
{assert(plist);ListNode* cur = plist->next;while (cur != plist){if (cur->data == x)return cur;cur = cur->next;}return NULL;
}
// 在pos位置前插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);ListNode* NewNode = BuyListNode(x);ListNode* PosPrev = pos->prev;PosPrev->next = NewNode;NewNode->prev = PosPrev;NewNode->next = pos;pos->prev = NewNode;
}
// 删除pos位置的结点
void ListErase(ListNode* pos)
{assert(pos);ListNode* PosPrev = pos->prev;ListNode* PosNext = pos->next;PosPrev->next = PosNext;PosNext->prev = PosPrev;free(pos);pos = NULL;
}

TestLish.c文件

#include"List.h"
void TestList01()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);Listprint(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);ListPopBack(plist);// ListPopBack(plist);Listprint(plist);ListPushFront(plist, 1);ListPushFront(plist, 2);ListPushFront(plist, 3);ListPushFront(plist, 4);Listprint(plist);ListPopFront(plist);ListPopFront(plist);ListPopFront(plist);ListPopFront(plist);// ListPopFront(plist);Listprint(plist);
}
void TestList02()
{ListNode* plist = ListInit();ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListNode* pos = ListFind(plist, 3);ListInsert(pos, 30);Listprint(plist);ListErase(pos);Listprint(plist);}
int main()
{TestList01();TestList02();
}

双向循环链表讲解及实现相关推荐

  1. 双向循环链表的讲解及实现(图解+代码/C语言)

    本次为大家分享的是双向循环链表的增删查改等系列操作. 目录 一.图解双向循环链表结构 二.分步实现 (1)创建并初始化 (2)链表元素打印 (3)头插和尾插 (4)判断链表为空 (5)头删和尾删 (6 ...

  2. 试编写一个将双向循环链表逆置的算法_图解:链表的快慢指针,解决 80% 的链表面试题!...

    一.前言 链表是基本的数据结构之一,它与数组不同,数组在内存中存储,需要一块连续的内容空间来存储,对内存的要求比较高.例如我们需要 100MB 大小的数组,内存中就必须有一段连续的 100MB 的内存 ...

  3. 双向循环链表:鸿蒙轻内核中数据的“驿站”

    本文分享自华为云社区<鸿蒙轻内核M核源码分析系列二 数据结构-双向循环链表>,原文作者:zhushy . 在学习OpenHarmony鸿蒙轻内核源代码的时候,常常会遇到一些数据结构的使用. ...

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

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

  5. 玩转数据结构之双向循环链表

    文章目录 一.前言 二.双向链表的实现 ①.定义节点 ②.创建新节点(BuyLTNode) ③.初始化链表(ListInit) ④.双向链表销毁(ListDestory) ⑤.双向链表打印(ListP ...

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

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

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

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

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

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

  9. 数据结构与算法(2-2)线性表之链式存储(单链表、静态链表、循环链表、双向循环链表)

    目录 一.单链表 1.存储方式 2.插入 3.删除 总代码: 二.静态链表 1.存储方式 2.插入 3.删除 4.遍历 总代码: 三.循环链表 总代码: 四.双向循环链表 1.存储方式: 2.插入和删 ...

  10. 链表 -- 双向循环链表(线性表)

    1,双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱.所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点.一般我们都构造双向循环 ...

最新文章

  1. java lambda 变量_java8新特性-lambda(变量捕获)
  2. 腾讯会议又一黑科技,屏蔽超过 200 种会议噪声是如何做到的?
  3. 全球移动SaaS市场规模5年将增170亿美元
  4. 使用apache的HttpClient进行http通讯,隐藏的HTTP请求头部字段是如何自动被添加的
  5. 让你的 Qt 桌面程序看上去更加 native(三):自定义 style
  6. ug链轮设计软件_正版UG软件,UG软件代理,正版UG软件模块功能介绍
  7. matlab信号系统响应实验,信号与系统实验(MATLAB版) (1)
  8. 谷歌浏览器好用的插件推荐
  9. mac下复制粘贴需要多次的问题
  10. 基于自适应决策算子的鲸鱼优化算法-附代码
  11. Linux 常用命令大全
  12. 查询用户活跃度表登录间隔30天的用户
  13. 初学rust——Tests
  14. IP查询和IPv6查询接口
  15. 利用动态二进制加密实现新型一句话木马之.NET篇(转)冰蝎
  16. IC芯片设计项目管理003:检查清单checklist的应用
  17. 图像分割 FCN(1):FCN网络讲解
  18. 对接阿里云sms短信服务发送验证码
  19. Google三驾马车:GFS、MapReduce和Bigtable
  20. Erlang NIF浅析

热门文章

  1. 仿真工具NS3的基本知识
  2. QThread线程详细用法
  3. STEP文件格式总结
  4. 网易云通信 java 登录_Java接入网易云信工具类
  5. 控制系统--线性定常数系统的传递函数
  6. CS224N WINTER 2022(二)反向传播、神经网络、依存分析(附Assignment2答案)
  7. 深度学习之基于Tensorflow2.0实现VGG16网络
  8. 搭建Vgg16训练CIFAR10数据集
  9. magisk下载里显示没有模块_太极Magisk模块
  10. 微信小程序实现上传图片的功能