带头结点的双向循环链表的实现

一、双向链表的概念

双向链表的每个结点有两个指针,一个指针指向该结点的前驱,一个指针指向该结点的后继。

二、双向链表的存储结构

//线性表的双向链表存储结构
typedef struct DuLNode
{ElemType data;DuLNode *prior, *next;
}DuLNode, *DuLinkList;

三、带有头结点的双向循环链表的样子

四、带头结点的双向循环链表的操作

五、操作的实现

1、 创建空的双向循环链表L

void InitList(DuLinkList &L)
{ // 产生空的双向循环链表LL = (DuLinkList)malloc(sizeof(DuLNode));//创建结点if (L != NULL)L->next = L->prior = L;elseexit(-1);
}

2、判断双循环链表是否为空

bool ListEmpty(DuLinkList L)
{ // 初始条件:线性表L已存在。//操作结果:若L为空表,则返回true,否则返回falseif (L->next == L && L->prior == L)return true;elsereturn false;
}

3、获取双循环链表的结点元素个数

int ListLength(DuLinkList L)
{ // 初始条件:L已存在。操作结果:返回L中数据元素个数//只利用后指针域即可int i = 0;DuLinkList p = L->next; // p指向第一个结点while (p != L) // 查看p是不是指向头结点,不是头结点就计数1{i++;p = p->next;}return i;
}

4、在带头结点的双链循环线性表L中第i个位置之前插入元素e

bool ListInsert(DuLinkList L, int i, ElemType e)
{ // 在带头结点的双链循环线性表L中第i个位置之前插入元素e,i的合法值为1≤i≤表长+1DuLinkList p = L, NewNode;if (i<1 || i>ListLength(L) + 1) // i超出范围,其值不合法return false;int j = 0;while (j < i - 1)// 在L中确定第i-1个结点的位置指针p{j++;p = p->next;}if (p == NULL) // p=NULL,即第i个元素的前驱不存在(设头结点为第1个元素的前驱)return false;NewNode = (DuLinkList)malloc(sizeof(DuLNode));if (NewNode == NULL)return false;NewNode->data = e;//新节点赋值NewNode->prior = p; // 新结点前指针域指向第i-1个结点NewNode->next = p->next;//新结点后指针域指向第i个结点p->next->prior = NewNode;//第i个结点的前指针域指向新结点p->next = NewNode;//第i-1个结点的后指针域指向新结点return true;
}

5、遍历输出结点值

void ListTraverse(DuLinkList L)
{DuLinkList p = L->next;//p指向第一个结点while (p != L){printf("  %d",p->data);p = p->next;}printf("\n");
}

6、获取第i个结点的值

ElemType GetElem(DuLinkList L, ElemType i)
{if (i<1||i>ListLength(L))//i不在链表结点范围之内{printf("输入的i值非法");return false;}DuLinkList p = L;int j = 0;while (j<i)//找到第i个结点的指针{j++;p = p->next;}return p->data;//返回地i和结点的值
}

7、反向遍历输出结点值

void ListTraverseBack(DuLinkList L)
{DuLinkList p = L->prior;//p指向第一个结点while (p != L){printf("  %d", p->data);p = p->prior;}printf("\n");
}

8、 查询结点值为cur_e的结点的直接前驱结点值

bool PriorElem(DuLinkList L, ElemType cur_e, ElemType &pre_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,//           否则操作失败,pre_e无定义DuLinkList q = NULL, p = L; // p指向头结点if (p->next->data == cur_e)//判断cur_e是否等于首结点值{printf("首结点没有前驱");return false;// 操作失败}while (p->next != L) // p的下一个结点不是头结点则指向下一个结点{    p = p->next;if (p->data == cur_e)//判后{pre_e = p->prior->data;return true;}}return false; // 操作失败
}

9、 查询结点值为cur_e的结点的直接后驱结点值

bool NextElem(DuLinkList L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,//           否则操作失败,next_e无定义DuLinkList p = L->next; // p指向首结点while (p->next != L) // p指向结点的指针域没有指向头结点(p的下一个结点不是头结点),只有最后一个结点的指针域指向头结点,即p不指向最后一个结点{if (p->data == cur_e)//判前{next_e = p->next->data;//满足条件获取后继结点值return true;}p = p->next;//不满足条件p指向下一个结点}return false; // 操作失败
}

10、双向链表L中返回第i个元素的地址。

DuLinkList GetElemP(DuLinkList L, int i) // 另加
{ // 在双向链表L中返回第i个元素的地址。i为0,返回头结点的地址。若第i个元素不存在,// 返回NULLint j;DuLinkList p = L; // p指向头结点if (i<0 || i>ListLength(L)) // i值不合法return NULL;for (j = 1; j <= i; j++)p = p->next;return p;
}

11、删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长

bool ListDelete(DuLinkList L,int i,ElemType &e)
{if (i<1 || i>ListLength(L)){printf("i的值不合法\n");return false;}DuLinkList p = L,q;int j = 0;while (j < i-1)//找到第i-1个结点{j++;p = p->next;}q = p->next;e = q->data;p->next = q->next;q->next->prior = p;free(q);return true;
}

12、清空双循环链表

void ClearList(DuLinkList L) // 不改变L
{ // 初始条件:L已存在。操作结果:将L重置为空表DuLinkList q, p = L->next; // p指向第一个结点while (p != L) // p没到表头{q = p->next;free(p);p = q;}L->next = L->prior = L; // 头结点的两个指针域均指向自身
}

13、销毁双循环链表

void DestroyList(DuLinkList &L)
{ // 操作结果:销毁双向循环链表LDuLinkList q, p = L->next; // p指向第一个结点while (p != L) // p没到表头{q = p->next;free(p);p = q;}free(L);L = NULL;
}

最后给出完整代码

// 带头结点的双向循环链表.cpp#include<stdio.h>
#include<stdlib.h>
#include<malloc.h> //定义数据类型;
typedef int ElemType;//线性表的双向链表存储结构
typedef struct DuLNode
{ElemType data;DuLNode *prior, *next;
}DuLNode, *DuLinkList;//函数声明
//1、 创建双向循环链表L
void InitList(DuLinkList &L);
//2、判断双循环链表是否为空
bool ListEmpty(DuLinkList L);
//3、获取双循环链表的结点元素个数
int ListLength(DuLinkList L);
//4、在带头结点的双链循环线性表L中第i个位置之前插入元素e
bool ListInsert(DuLinkList L, int i, ElemType e);
//5、遍历输出结点值
void ListTraverse(DuLinkList L);
//6、获取第i个结点的值
ElemType GetElem(DuLinkList L, ElemType i);
//7、反向遍历输出结点值
void ListTraverseBack(DuLinkList L);
//8、 查询结点值为cur_e的结点的直接前驱结点值
bool PriorElem(DuLinkList L, ElemType cur_e, ElemType &pre_e);
//9、 查询结点值为cur_e的结点的直接后驱结点值
bool NextElem(DuLinkList L, ElemType cur_e, ElemType &next_e);
//10、双向链表L中返回第i个元素的地址。
DuLinkList GetElemP(DuLinkList L, int i);
//11、删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长
bool ListDelete(DuLinkList L, int i, ElemType &e);
//12、清空双循环链表
void ClearList(DuLinkList L);// 不改变Lint main()
{DuLinkList L;InitList(L);if (ListEmpty(L)){printf("链表为空!\n");}else{printf("链表不为空\n");}printf("链表的长度为 %d\n",ListLength(L));for (int i = 1; i <= 10; i++){ListInsert(L,i,2*i);}ListTraverse(L);int i = 10;printf("获取第%d个结点值为 %d\n", i,GetElem(L, i));ListTraverseBack(L);DuLinkList p = GetElemP(L,  i);printf("第%d个结点的地址是%p\n",i,p);ElemType e = 0;ListDelete( L,  i, e);printf("删除的第%d个结点的值是%d\n",i,e);ClearList(L);ListTraverse(L);//DestroyList(L);//ListTraverse(L);return 0;
}
//1、 创建双向循环链表L
void InitList(DuLinkList &L)
{ // 产生空的双向循环链表LL = (DuLinkList)malloc(sizeof(DuLNode));//创建结点if (L != NULL)L->next = L->prior = L;elseexit(-1);
}//2、判断双循环链表是否为空
bool ListEmpty(DuLinkList L)
{ // 初始条件:线性表L已存在。//操作结果:若L为空表,则返回true,否则返回falseif (L->next == L && L->prior == L)return true;elsereturn false;
}
//3、获取双循环链表的结点元素个数
int ListLength(DuLinkList L)
{ // 初始条件:L已存在。操作结果:返回L中数据元素个数//只利用后指针域即可int i = 0;DuLinkList p = L->next; // p指向第一个结点while (p != L) // 查看p是不是指向头结点,不是头结点就计数1{i++;p = p->next;}return i;
}// 4、在带头结点的双链循环线性表L中第i个位置之前插入元素e
bool ListInsert(DuLinkList L, int i, ElemType e)
{ // 在带头结点的双链循环线性表L中第i个位置之前插入元素e,i的合法值为1≤i≤表长+1// 改进算法2.18,否则无法在第表长+1个结点之前插入元素DuLinkList p = L, NewNode;if (i<1 || i>ListLength(L) + 1) // i超出范围,其值不合法return false;int j = 0;while (j < i - 1)// 在L中确定第i-1个结点的位置指针p{j++;p = p->next;}if (p == NULL) // p=NULL,即第i个元素的前驱不存在(设头结点为第1个元素的前驱)return false;NewNode = (DuLinkList)malloc(sizeof(DuLNode));if (NewNode == NULL)return false;NewNode->data = e;//新节点赋值NewNode->prior = p; // 新结点前指针域指向第i-1个结点NewNode->next = p->next;//新结点后指针域指向第i个结点p->next->prior = NewNode;//第i个结点的前指针域指向新结点p->next = NewNode;//第i-1个结点的后指针域指向新结点return true;
}
//5、遍历输出结点值
void ListTraverse(DuLinkList L)
{DuLinkList p = L->next;//p指向第一个结点while (p != L){printf("  %d",p->data);p = p->next;}printf("\n");
}
//6、获取第i个结点的值
ElemType GetElem(DuLinkList L, ElemType i)
{if (i<1||i>ListLength(L))//i不在链表结点范围之内{printf("输入的i值非法");return false;}DuLinkList p = L;int j = 0;while (j<i)//找到第i个结点的指针{j++;p = p->next;}return p->data;//返回地i和结点的值
}//7、反向遍历输出结点值
void ListTraverseBack(DuLinkList L)
{DuLinkList p = L->prior;//p指向第一个结点while (p != L){printf("  %d", p->data);p = p->prior;}printf("\n");
}//8、 查询结点值为cur_e的结点的直接前驱结点值
bool PriorElem(DuLinkList L, ElemType cur_e, ElemType &pre_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱,//           否则操作失败,pre_e无定义DuLinkList q = NULL, p = L; // p指向头结点if (p->next->data == cur_e)//判断cur_e是否等于首结点值{printf("首结点没有前驱");return false;// 操作失败}while (p->next != L) // p的下一个结点不是头结点则指向下一个结点{    p = p->next;if (p->data == cur_e)//判后{pre_e = p->prior->data;return true;}}return false; // 操作失败
}//9、 查询结点值为cur_e的结点的直接后驱结点值
bool NextElem(DuLinkList L, ElemType cur_e, ElemType &next_e)
{ // 初始条件:线性表L已存在// 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继,//           否则操作失败,next_e无定义DuLinkList p = L->next; // p指向首结点while (p->next != L) // p指向结点的指针域没有指向头结点(p的下一个结点不是头结点),只有最后一个结点的指针域指向头结点,即p不指向最后一个结点{if (p->data == cur_e)//判前{next_e = p->next->data;//满足条件获取后继结点值return true;}p = p->next;//不满足条件p指向下一个结点}return false; // 操作失败
}
//10、双向链表L中返回第i个元素的地址。
DuLinkList GetElemP(DuLinkList L, int i) // 另加
{ // 在双向链表L中返回第i个元素的地址。i为0,返回头结点的地址。若第i个元素不存在,// 返回NULLint j;DuLinkList p = L; // p指向头结点if (i<0 || i>ListLength(L)) // i值不合法return NULL;for (j = 1; j <= i; j++)p = p->next;return p;
}
//11、删除带头结点的双链循环线性表L的第i个元素,i的合法值为1≤i≤表长
bool ListDelete(DuLinkList L,int i,ElemType &e)
{if (i<1 || i>ListLength(L)){printf("i的值不合法\n");return false;}DuLinkList p = L,q;int j = 0;while (j < i-1)//找到第i-1个结点{j++;p = p->next;}q = p->next;e = q->data;p->next = q->next;q->next->prior = p;free(q);return true;
}
//12、清空双循环链表
void ClearList(DuLinkList L) // 不改变L
{ // 初始条件:L已存在。操作结果:将L重置为空表DuLinkList q, p = L->next; // p指向第一个结点while (p != L) // p没到表头{q = p->next;free(p);p = q;}L->next = L->prior = L; // 头结点的两个指针域均指向自身
}
//13、销毁双循环链表
void DestroyList(DuLinkList &L)
{ // 操作结果:销毁双向循环链表LDuLinkList q, p = L->next; // p指向第一个结点while (p != L) // p没到表头{q = p->next;free(p);p = q;}free(L);L = NULL;
}

数据结构笔记(六)-- 双向链表相关推荐

  1. 数据结构笔记--线性表定义与实现(Swift)

    数据结构笔记系列 数据结构笔记-两个有序链表合并成一个有序链表 线性表   线性表是最常用且最简单的一种数据结构,简言之,一个线性表是 n 个数据元素的有序序列. 特点 只有一个首结点和尾结点: 除首 ...

  2. 数据结构笔记(王道考研) 第八章:排序

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  3. 数据结构笔记(王道考研) 第五章:树和二叉树

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  4. opencv 手选roi区域_【opencv学习笔记六】图像的ROI区域选择与复制

    图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...

  5. 一、考研数据结构笔记——引言及目录

    一.关于我理解的数据结构 1. 引言 本人自2021年3月准备考研,考研主要是为了提升学历,本科院校不是理想.迫切需要提高学历. 写这刊博客,主要是总结我考研路上对数据结构的一些理解,以及为了方便我后 ...

  6. 数据结构笔记(王道考研) 第一章:绪论

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  7. 数据结构笔记(王道考研) 第七章:查找

    大部分内容基于中国大学MOOC的2021考研数据结构课程所做的笔记,该课属于付费课程(不过盗版网盘资源也不难找...).后续又根据23年考研的大纲对内容做了一些调整,将二叉排序树和平衡二叉树的内容挪到 ...

  8. 浙大2020年Mooc数据结构笔记--第三讲 树(下)

    浙大2020年Mooc数据结构笔记–第三讲 树(下) 〇.前言 这几天开始跟着学数据结构,鉴于当初数据结构实在学的太弱,加之这项工作算是为大家之后复习.机试铺路.确实是一个迫切需要做的大规模工作.行胜 ...

  9. libevent学习笔记六:libevent核心事件event

    libevent学习笔记六:libevent核心事件event 前面对reactor模式.事件处理流程.libevent源代码结构等有了高层的认识后,接下来将详细介绍libevent的核心结构even ...

  10. Ethernet/IP 学习笔记六

    Ethernet/IP 学习笔记六 EtherNet/IP defines two primary types of communications: explicit and implicit (Ta ...

最新文章

  1. Android 使用android-support-multidex解决Dex超出方法数的限制问题,让你的应用不再爆棚
  2. Java利用JNI调用c++代码简易例子演示
  3. TensorFlow的新生!
  4. MySql 你知道事务隔离是怎么回事吗?
  5. 基于JAVA+SpringMVC+Mybatis+MYSQL的同学录管理系统
  6. python中可迭代对象_什么是python中的可迭代对象(iterable object)?
  7. java jdk学习_Java学习第一步:JDK环境搭建(纯小白向)
  8. 仓库5s管理推行难点分析
  9. 华为研发岗位两轮面试的准备(本科生,已经拿到offer,月薪20k,15薪)
  10. linux修改文件类型和权限
  11. 游戏开发unity资源管理系列:查看AssetBundle的工具-AssetStudio
  12. 我又拖后腿了, 2月全国程序员平均工资13716元!
  13. java中常用的摘要算法
  14. python matplotlib 万花筒画板
  15. Win Server 2003搭建Sql注入环境
  16. 二进制转八进制公式计算机,2进制转8进制(二进制转8进制公式)
  17. aip pytesseract识别图片中的文字
  18. dojo query 实现Ajax,Dojo Query 详解
  19. adb工具的安装与常用命令
  20. 微软经典面试题(数字翻译为中文)

热门文章

  1. bzoj 3393 bzoj 1644: [Usaco2007 Oct]Obstacle Course 障碍训练课(BFS)
  2. C++中实现精度的控制和输出 showpoint<<或者fixed<<setprecision()用法
  3. Prometheus Alertmanager报警组件
  4. M文件-函数的参数-传值还是传址/检查输入参数的个数/传递参数/输出参数
  5. vivado下block design重新整理布局regenerate layout
  6. 贺利坚老师汇编课程27笔记(二):loop和[bx]访问连续内存单元
  7. AD小技巧:update changes to PCB后如何调整布线
  8. mysql不同服务器查询_实战操作SQL Server连接查询不同服务器表数据
  9. GoogLeNet网络的Pytorch实现
  10. 测开之路二十二:迭代器、生成器