C++实现双向循环链表
本次博文是关于利用C++模板的方式实现的双向循环链表以及双向循环链表的基本操作,在之前的博文C++语言实现双向链表中,已经给大家分析了双向循环链表的结构,并以图示的方式给大家解释了双向循环链表的基本操作。本篇文章利用C++实现了双向循环链表的基本操作,其中包括:
| 双向循环链表 | 实现的功能 |
|---|---|
| 头部插入结点建立链表 | 尾部插入结点建立链表 |
| 实现指定位置插入结点 | 查找给定数值是否存在 |
| 删除指定位置的结点 | 修改指定位置的结点 |
| 双向链表的长度 | 打印双向链表 |
定义双向链表的结点
双向循环链表的结点由三部分构成,用于指向当前节点的直接前驱节点的指针域,用于存储数据元素的数据域 ,以及用于指向当前节点的直接后继节点的指针域。
在之前的C++语言实现双向链表时已经给大家解释了封装的结点的特点,不需要作太大的改变,我们需要封装一个结点类,定义了结点的三个要素,并利用构造函数实现初始化,另外,考虑到在双向循环链表中要用到结点类,所以将双向链表类定义为结点的友元类。
template<class T>
class doubleCircularLinkedList;//声明一下双向循环链表,以免定义友元时报错
template <class T>
class doubleCircularLinkedListNode
{
private:
doubleCircularLinkedListNode<T> *prior;//双向结点前驱指针指向该结点的前驱结点
T data;//储存结点数据
doubleCircularLinkedListNode<T> *next;//双向结点的后驱指针指向该结点的后继结点
//将双向循环链表类定义为结点的友元类
friend class doubleCircularLinkedList<T>;
public:
//结点的无参构造函数,将结点指针域初始化为NULL
doubleCircularLinkedListNode()
{
prior = NULL;
next = NULL;
}
//结点的有参构造函数,初始化指针域和数据域
doubleCircularLinkedListNode(T _data,doubleCircularLinkedListNode<T> *_prior = NULL,doubleCircularLinkedListNode<T> *_next = NULL)
{
prior = _prior;//初始化前驱指针
data = _data;//初始化数据域
next = _next;//初始化后继指针
}
~doubleCircularLinkedListNode()
{
prior = NULL;
next = NULL;
}
};
双向链表的基本操作
本次实现的操作跟双向链表实现的操作基本一样,实现了双向循环链表头部插入结点, 尾部插入结点,指定位置插入结点建立链表, 查找给定数值的指定位置,删除指定位置的结点,修改指定位置的结点,双向循环链表的长度,打印双向循环链表,接下来逐一进行讲解实现:
头部插入结点建立链表
实现双向循环链表的头部插入结点,之前的双向链表因为在头部和尾部的指针都是指向NULL的,所以需要分情况来处理,然而双向循环链表没有元素时这两个指针都是指向自身的,因此并不需要分情况处理,都需要修改四个指针。
因此,头部插入结点实现如下:
template<class T>
bool doubleCircularLinkedList<T>::insertNodeByhead(T item)
{
//创建一个新的结点
doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
if (newNode == NULL){
cout << "内存分配失败,新结点无法创建" << endl;
return false;
}
else{
newNode->prior = headNode;
newNode->next = headNode->next;
headNode->next->prior=newNode;
headNode->next = newNode;
return true;
}
}
尾部插入结点建立链表
在尾部插入结点,当然第一步需要找到最后一个结点,然后在其后进行插入,调整四个指针即可。
template<class T>
bool doubleCircularLinkedList<T>::insertNodeBytail(T item)
{
//创建一个新的结点
doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
if (newNode == NULL){
cout << "内存分配失败,新结点无法创建" << endl;
return false;
}
//首先找到最后一个结点
doubleCircularLinkedListNode<T>* lastNode = headNode;
while(lastNode->next != headNode)
{
lastNode = lastNode->next;//没找到就一直循环
}
//找到之后调整四个指针
headNode->prior = newNode;
newNode->next = headNode;
lastNode->next = newNode;
newNode->prior = lastNode;
return true;
}
实现指定位置插入结点
在指定位置插入只需要两步走,首先也是找到指定的位置,然后就是插入新结点的指针的调整,中间插入是最复杂的,都需要调整四个指针,最后让新结点与前继结点建立关系,实现新结点的插入。
template<class T>
bool doubleCircularLinkedList<T>::insertNode(T item,int n)
{
if(n<1){
cout<<"输入的非有效位置!"<<endl;
return false;
}
doubleCircularLinkedListNode<T>* pMove = headNode;//创建一个新的指针,设置为游标指针
//首先找到插入位置
for(int i=1;i<n;i++)
{
pMove = pMove->next;
if(pMove == NULL&& i<=n)
{
cout<<"插入位置无效!"<<endl;
return false;
}
}
//创建一个新的结点
doubleCircularLinkedListNode<T>* newNode = new doubleCircularLinkedListNode<T>(item);
if (newNode == NULL){
cout << "内存分配失败,新结点无法创建" << endl;
return false;
}
//插入新的结点
newNode->next = pMove->next;
if (pMove->next != headNode)
{
pMove->next->prior = newNode;
}
newNode->prior = pMove;
pMove->next = newNode;
return true;
}
查找给定数值是否存在
查找给定元素,也就是一个遍历双向循环链表的过程,从头结点的下一个结点开始遍历,毕竟第一个头结点是没有储存数据项的。
template<class T>
bool doubleCircularLinkedList<T>::findData(T item)
{
doubleCircularLinkedListNode<T> *pMove = headNode->next; //设置游标指针
doubleCircularLinkedListNode<T> *pMoveprior = headNode;//指定结点前一个结点的指针
//找到指定位置
while(pMove->data != item)
{
pMoveprior = pMove;
pMove = pMoveprior->next;
//如果没有找到特殊处理
if(pMove == headNode)
{
return false;
}
}
return true;
}
删除指定位置的结点
删除指定的结点,第一步查找到删除的结点,需要定义一个删除指针临时指向将要删除的结点,最后指针处理删除之后别忘了释放该结点空间。
template<class T>
bool doubleCircularLinkedList<T>::deleteData(int n)
{
if (n<1||n>getLength())
{
cout << "输入非有效位置" << endl;
return false;
}
doubleCircularLinkedListNode<T> * pMove = headNode;//设置游标指针
doubleCircularLinkedListNode<T> * pDelete;
//查找删除结点的位置
for (int i = 1; i <= n; i++)
{
pMove = pMove->next; //游标指针后移
}
//删除结点
pDelete = pMove;
pMove->prior->next = pDelete->next;
pMove->next->prior = pDelete->prior;
delete pDelete;//释放空间
return true;
}
修改指定位置的结点
修改指定位置的结点数据,当然还是得找到指定位置,然后对其进行修改,修改之后将原来的数据以引用的形式返回。
template<class T>
bool doubleCircularLinkedList<T>::changeListElements(int n,T item,T &x)
{
if (n<1||n>getLength())
{
cout << "输入非有效位置" << endl;
return false;
}
doubleCircularLinkedListNode<T> *pMove = headNode->next; //设置游标指针
for(int i=1;i<n;i++)//找到指定位置1
{
pMove = pMove->next;
}
x = pMove->data;
pMove->data = item;
return true;
}
双向循环链表的长度
计算双向链表的长度的函数,在双向链表的私有成员封装了一个变量length,以此来记录双向链表的长度,遍历双向链表,逐一进行计算结点数就是双向链表的长度。
template<class T>
int doubleCircularLinkedList<T>::getLength()
{
doubleCircularLinkedListNode<T> *pMove = headNode->next; //设置游标指针
int length=0;
//遍历链表,计算结点数
while(pMove != headNode)
{
pMove = pMove->next; //游标指针后移
length++; //计算length
}
return length;
}
打印双向循环链表
template<class T>
void doubleCircularLinkedList<T>::printLinkedlist()
{
//从第二个结点开始打印,表头不含数据
doubleCircularLinkedListNode<T>* pMove = headNode->next;
while(pMove !=headNode)//如果pMove->next != headNode这样写,最后一个结点是不会打印的
{
cout<<pMove->data<<" ";
pMove = pMove->next;//移动指针
}
cout<<endl;
}
以上就是我简要的给大家分享的C++实现双向循环链表,因为实现了双向链表,所以基本上实现思路差不多,唯一的不同就是在循环一词不同,这一不同就是头结点的前驱指针和尾结点的后驱指针指向不同,要是还是不太清楚的可以去那篇博客看看。本次的完整代码已经全部上传到github! (C++实现双向循环链表),还想了解其他的数据结构实现的可以去我的博客,我们一起讨论啊,一起进步!
C++实现双向循环链表的更多相关文章
- 双向链表、双向循环链表的JS实现
关于链表简介.单链表.单向循环链表.JS中的使用以及扩充方法: 单链表.循环链表的JS实现 关于四种链表的完整封装: https://github.com/zhuwq585/Data-Structu ...
- C语言通用双向循环链表操作函数集
说明 相比Linux内核链表宿主结构可有多个链表结构的优点,本函数集侧重封装性和易用性,而灵活性和效率有所降低. 可基于该函数集方便地构造栈或队列集. 本函数集暂未考虑并发保护. 一 ...
- 双向循环链表的Java版本实现
1.单项循环列表 单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环.和单链表相比,循环单链表的长处是从链尾到链 ...
- c语言编程之双向循环链表
双向循环链表就是形成两个环,注意每个环的首尾相连基本就可以了. 程序中采用尾插法进行添加节点. #include<stdio.h> #include<stdlib.h> #de ...
- Linux内核中的通用双向循环链表
开发中接触Linux越来越多,休息放松之余,免不了翻看翻看神秘的Linux的内核.看到双向链表时,觉得挺有意思的,此文记下. 作为众多基础数据结构中的一员,双向循环链表在各种“教科书”中的实现是相当的 ...
- java与数据结构(4)---java实现双向循环链表
线性表之链式存储结构双向循环链表 双向循环链表:每个结点包含了数据.直接前驱地址指针和直接后驱地址指针,头结点的直接前驱指向尾结点,尾结点的直接后驱指向头结点,头尾相连构成一个可正可反的圆环.可以形象 ...
- 基于visual Studio2013解决算法导论之025双向循环链表
题目 双向循环链表 解决代码及点评 #include <stdio.h> #include <stdlib.h> #include <time.h> #in ...
- 双向循环链表(C语言描述)(四)
下面以一个电子英汉词典程序(以下简称电子词典)为例,应用双向循环链表.分离数据结构,可以使逻辑代码独立于数据结构操作代码,程序结构更清晰,代码更简洁:电子词典的增.删.查.改操作分别对应于链表的插入. ...
- 双向循环链表(C语言描述)(一)
双向循环链表是链表的一种,它的每个节点也包含数据域和指针域.为了方便程序维护,可以单独为数据域定义一种数据类型,这里以整型为例: typedef int LinkedListData; 双向循环链表( ...
- python实现 双向循环链表
最近身边的朋友在研究用python来实现数据结构.遇到一个问题就是双向循环链表的实现,改指向的时候总是发蒙. 我自己尝实现了一个python的双向循环链表.附上代码,希望对大家有帮助. 如果不懂什么是 ...
随机推荐
- vscode style内置auto会导致eslint格式化 对不齐报错
"files.associations": { "*.vue": "vue", // "*.js": "jav ...
- keep-alive 必须 页面有name 要不缓存不住数据
keep-alive 必须 页面有name 要不缓存不住数据
- 交换机三种模式Access、Hybrid和Trunk
[端口介绍] 种链路类型:access.trunk.hybird 个VLAN,一般用于连接计算机端口: Trunk类型端口:可以允许多个VLAN通过,可以接收和发送多个VLAN 报文, 一般用于交换机 ...
- Python习题集(十六)
每天一习题,提升Python不是问题!!有更简洁的写法请评论告知我! https://www.cnblogs.com/poloyy/category/1676599.html 题目 写一个函数repl ...
- C++ 人脸识别系统的浅理解
机器学习 机器学习的目的是把数据转换成信息. 机器学习通过从数据里提取规则或模式来把数据转成信息. 人脸识别 人脸识别通过级联分类器对特征的分级筛选来确定是否是人脸. 每个节点的正确识别率很高,但正确 ...
- MySQL基础篇(07):用户和权限管理,日志体系简介
本文源码:GitHub·点这里 || GitEE·点这里 一.MySQL用户 1.基础描述 在数据库的使用过程中,用户作为访问数据库的鉴权因素,起到非常重要的作用,安装MySQL时会自动生成一个roo ...
- 【2020-03-28】Dubbo源码杂谈
前言 本周空闲时间利用了百分之六七十的样子.主要将Dubbo官网文档和本地代码debug结合起来学习,基本看完了服务导出.服务引入以及服务调用的过程,暂未涉及路由.字典等功能.下面对这一周的收获进行一 ...
- ContOS7中使用Nginx进行TCP反向代理
一.安装Nginx 1.下载:http://nginx.org/en/download.html wget http://nginx.org/download/nginx-1.16.1.tar.gz ...
- Pandas和Numpy的一些金融相关的操作(一)
Pandas和Numpy的一些金融相关的操作 给定一个净值序列,求出最大回撤 # arr是一个净值的np.ndarray i = np.argmax( (np.maximum.acumulate(ar ...
- CodeForces - 1244D 树(一条链)的染色
题意:给一个无向的无环的树,需要用三种颜色将他染色,相邻的三个点不能有重复的颜色.给出每个点染成每种颜色的花费,求最小的染色花费,如果给的图不能按要求染色,输出-1. 思路:只有三种颜色,相邻三个点还 ...