作者:低调
作者宣言:写好每一篇博客

文章目录

  • 前言
  • 一、带头双向循环链表的实现
    • 1.1创建返回链表的头结点
    • 1.2开辟一个新的结点
    • 1.3双向链表的销毁
    • 1.4双向链表的打印
    • 1.5双向链表尾插
    • 1.6双向链表的尾删
    • 1.7双向链表的头插
    • 1.8双向链表的头删
    • 1.9查找元素的位置
    • 1.10双向链表在pos的前面进行插入
    • 1.11双向链表删除pos位置的节点
  • 二、双向链表的运行结果
  • 总结

前言

亲爱的读者们,今天我又来更新好文了,通过上一篇博客,我重点介绍了链表的相关知识link大家如果没看过点开链接去查看,今天我重点讲述带头双向循环链表,此结构虽然复杂,但实现起来特别的简单,那我们话不多说,进入正题。


以下是本篇文章正文内容,下面案例可供参考

一、带头双向循环链表的实现


我们看到他的结构,有一个哨兵位的头结点,他不存储有效数据,不需要传二级指针。
我们先来看看怎么定义一个双向链表:
我们以存入整型为例:

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{LTDataType _data;struct ListNode* next;//指向下一个结点struct ListNode* prev;//指向上一个结点}ListNode;

注:这里还是老样子,创建两个原文件,一个头文件。相信大家都知道为什么要这么创建了,在讲解顺序表的时候我有解释过。

让我们来看看他要实现那些接口:

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* plist);
// 双向链表打印
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);

1.1创建返回链表的头结点

//代码一
void ListCreate(ListNode** pphead)//初始化
{*pphead = BuyListNode(0);(*pphead)->prev = *pphead;(*pphead)->next = *pphead;
}//代码二
ListNode* ListCreate()
{ListNode* phead = BuyListNode(0);//将其初始化为0;phead->next = phead;phead->prev = phead;return phead;
}

创建头节点相当于初始化,这两种代码都可以实现初始化的效果

1.2开辟一个新的结点

ListNode*BuyListNode(LTDataType x)
{ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));if (newNode == NULL){printf("开辟内存失败\n");}newNode->data = x;newNode->prev = NULL;newNode->next = NULL;return newNode;
}

这个代码可以说跟单链表开辟一个新结点几乎一样。这里不做过多的介绍了。

1.3双向链表的销毁

void ListDestory(ListNode** pphead)
{assert(*pphead);ListNode* cur =(*pphead)->next;while (cur != *pphead){ListNode* next = cur->next;free(cur);cur = next;}free(*pphead);*pphead = NULL;
}

销毁的前一个的时候,记得保存下一个的地址,防止释放后找不到下一个的地址,造成程序崩溃。头结点需要最后释放,他是作为循环停止的标志。

1.4双向链表的打印

void ListPrint(ListNode* phead)
{ListNode* cur = phead->next;while (cur!= phead){printf("%d->", cur->data);cur = cur->next;}printf("NULL\n");
}

这跟单链表的打印几乎一模一样。

1.5双向链表尾插

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


我们进行尾插的时候需要找到最后一个结点,在单链表的时候,尾结点需要进行遍历才能找到,但得益于循环的好处,尾结点就是头结点的前一个。对照图来看看代码是怎么实现的

1.6双向链表的尾删

void ListPopBack(ListNode* phead)//尾删
{assert(phead);assert(phead->next != phead);//判断是否删除没了ListNode* tail = phead->prev;tail->prev->next = phead;phead->prev = tail ->prev;free(tail);tail=NULL;
}


代码实现起来都很简单。

1.7双向链表的头插

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


要注意第一步和第二步,反过来写也行,但要保存head的下一个地址,看这图去理解代码。

1.8双向链表的头删

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

1.9查找元素的位置

ListNode* ListFind(ListNode* phead,LTDataType x)//查找元素的位置
{assert(phead);ListNode* cur = phead->next;while (cur!=phead){if (cur->data == x){return cur;}cur = cur->next;}return -1;}

1.10双向链表在pos的前面进行插入

void ListInsert(ListNode* pos, LTDataType x)//从查找位置的前一个插入
{assert(pos);ListNode* newNode = BuyListNode(x);pos->prev->next = newNode;newNode->prev = pos->prev;newNode->next = pos;pos->prev = newNode;
}

1.11双向链表删除pos位置的节点

void ListErase(ListNode* pos)//把查找的位置删除删除
{assert(pos);ListNode* ppos = pos->prev;ppos->next = pos->next;pos->next->prev = ppos;free(pos);
}

注:前面几个接口作者没有画图了,我相信读者如果理解了单链表的实现原理,可以自己看着代码去理解。你们也要锻炼画图的能力。数据结构这一块代码不是太难理解,主要就是画图梳理思路。

二、双向链表的运行结果

#include "List.h"
int main()
{//ListNode* pList = NULL;ListNode* pList = ListInit();//两种初始化的方式ListInit(&pList);ListPushBack(pList,1);ListPushBack(pList,2);ListPushBack(pList,3);printf("尾插后的结果:");ListPrint(pList);ListPushFront(pList, 5);ListPushFront(pList, 6);ListPushFront(pList, 7);printf("头插后的结果:");ListPrint(pList);ListPopBack(pList);ListPopBack(pList);printf("尾删后的结果:");ListPrint(pList);ListPopFront(pList);ListPopFront(pList);printf("头删后的结果:");ListPrint(pList);ListNode* pos = ListFind(pList, 5);if (pos != -1){printf("找到了,改成了10\n");//pos->data = 10;//充当了修改;}else{printf("没有找到\n");}printf("修改后的结果:");ListPrint(pList);ListInsert(pos, 20);printf("在pos之前插入的结果:");ListPrint(pList);ListErase(pos);printf("删除pos位置处的结果:");ListPrint(pList);ListDestory(&pList);return 0;
}

总结

双向链表在实际生活用的比较广泛,所以我们要熟练的掌握他的使用。链表相关的知识点我在单链表部分已经做了详细的解释,这里我就不过多的解释了,这里我给大家留几道选择题:

1.在一个长度为n的顺序表中删除第i个元素,要移动_______个元素。如果要在第i个元素前插入一个
元素,要后移_________个元素。
A n-i,n-i+1 B n-i+1,n-i C n-i,n-i D n-i+1,n-i+1

2.取顺序表的第i个元素的时间同i的大小有关()
A 对 B 错

3.在一个具有 n 个结点的有序单链表中插入一个新结点并仍然保持有序的时间复杂度是 。 A O(1) B O(n) C O(n2) D O(nlog2n)

4.下列关于线性链表的叙述中,正确的是( )。
A 各数据结点的存储空间可以不连续,但它们的存储顺序与逻辑顺序必须一致
B 各数据结点的存储顺序与逻辑顺序可以不一致,但它们的存储空间必须连续
C 进行插入与删除时,不需要移动表中的元素
D 以上说法均不正确

5.设一个链表最常用的操作是在末尾插入结点和删除尾结点,则选用()最节省时间。
A 单链表
B 单循环链表
C 带尾指针的单循环链表
D 带头结点的双循环链表

6.链表不具有的特点是()。
A 插入、删除不需要移动元素
B 不必事先估计存储空间
C 可随机访问任一元素
D 所需空间与线性表长度成正比

7.在一个单链表中,若删除 P 所指结点的后续结点,则执行? A p = p->next;p->next = p->next->next; B p->next = p->next; C p->next = p->next->next; D p = p->next->next

8.一个单向链表队列中有一个指针p,现要将指针r插入到p之后,该进行的操作是____。 A p->next=p->next->next
B r->next=p;p->next=r->next
C r->next=p->next;p->next=r D r=p->next;p->next=r->next
E r->next=p;p->next=r F p=p->next->next

答案:1.A
2.B
3.B
4.C
5.D
6.C
7.C
8.C

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

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

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

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

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

  3. 数据结构-带头双向循环链表(增删查改详解)

    在上一篇博客中,详细介绍了单链表的增删查改,虽然单链表的结构简单,但是用起来却不是那么顺手.因此根据单链表的种种缺点,这篇博客所介绍的带头双向循环链表将会带来极大的优化. 上图就是带头双向循环链表的主 ...

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

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

  5. 【数据结构】带头+双向+循环链表的 增,删,查,改 的实现

    #include <iostream>using namespace std;int main() {typedef int ListType;typedef struct ListNod ...

  6. 【数据结构】带头双向循环链表

    各位读者们好久不见了,咋们接着上一期链表来,今天来实现一下链表最难的结构,同时也是实现起来最简单的结构--带头双向循环链表.话不多说,进入主题 文章目录 前言 实现带头双向循环链表 DList.h头文 ...

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

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

  8. 数据结构:二叉搜索树的增删查改

    二叉搜索树的增删查改 二叉搜索树(Binary Search Tree) 基本操作之查找(Update) 基本操作之修改(Update) 基本操作之增加(Create) 基本操作之删除(Delete) ...

  9. 数据结构C语言实现顺序表——增删查改操作实现详解

    顺序表 顺序表是什么? 顺序表是将元素顺序地存放在一块连续的存储区里,元素间的顺序关系由它们的存储顺序自然表示.实现增删查改的功能. 顺序表所需的头文件: #include<stdio.h> ...

最新文章

  1. LaTex中的documentclass{type}应该填什么
  2. 【330天】跃迁之路——程序员高效学习方法论探索系列(实验阶段88-2018.01.01)...
  3. pycharm新建文件夹时新建python package和新建directory有什么区别?
  4. H5_canvas与svg
  5. 启动T0运行的C语言语句是,单片机填空题期末复习。、
  6. [设计模式] ------ 原型模式(浅拷贝和深拷贝)
  7. 封装Cell(-去掉TableView那些碍眼的分割线)
  8. 换服务器系统怎么迁移,更换服务器时,数据迁移的方法
  9. javascript实现中国地图
  10. 一些可以使用的网上图片地址
  11. ios中获得UUID的方法
  12. Hero image网站转化这么高?21个最佳案例给你参考
  13. 厚积薄发 臻于至善,用友U9 cloud“王者归来”
  14. 【图像增强】Learning Enriched Features for Real Image Restoration and Enhancement 阅读笔记
  15. 维度表和事实表的含义
  16. 什么叫克隆人_【语文阅读理解】到底什么是克隆人
  17. mac完全卸载tuxera
  18. 【带移动搜索功能】织梦dedecms手机WAP插件专业版 织梦自动建手机WAP站 PC+WAP数据同步更新 访问自动跳转
  19. linux db2删除表字段命令,DB2—alter追加/删除/重置column操作
  20. ubuntu下PPPOE环境搭建

热门文章

  1. TL437x-IDK基于AM437x的FPGA与ARM通信测试
  2. 小数为何叫成浮点数 ?
  3. 为什么大部分小数在计算机中是不准确的
  4. Unity游戏系统之-RPG游戏剧情呈现策略
  5. Ubuntu20.04修改任务栏位置
  6. JUC-线程池理解与学习
  7. ldf文件怎么储存到mysql中_如何转移数据库MDF和LDF文件
  8. 植物大战僵尸基于OpenCv实现
  9. Linux主目录没有权限打开解决方法
  10. 软件测试员这些坑一定要记住了,不要再往里面掉了