在C++里,存在多种类型的表,其中有一种线性表,链表则是一种线性表,正如它的名字一样,链表的样子就像是用一条链子串起来的表(这里,我主要讲的是双循环链表)

而用来连接链子的每个环节的是指针,在最基本的单向不循环链表中,有一根单向指针next,这根指针的作用显而易见,就是串联上下两个节点。

说到串联节点,那么必不可少的自然是节点,节点又如何创建呢。首先,我们要创建一个节点类(class Node),在双循环链表中,想要实现双循环,那么就要求要有双向效果,也就是用来指向的指针应该要有两根,一根指向本节点的下一个节点(next),一根指向本节点的上一个节点(prior),以此达到双向的效果

这里,我以一个记录学生信息的双循环链表作为例子

class Node
{
public:int ID;string name;Node * next;Node * prior;Node() :next(nullptr), prior(nullptr) {}Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}void display(){cout << "ID:" << ID << endl << "Name:" << name << endl;}
};

在这个类里面,要有三种构造函数,无参构造主要是为了在链表类里面创建头结点使用,第一类有参构造是为了主函数内创建节点,而第二类构造则是拷贝构造,目的在于拷贝节点信息。

创建完节点,需要做的便是将节点串联起来。串联节点,首先需要有一个起点,这个起点就是一个标志性作用,在循环链表里面,头结点既是开头也是结尾,起到关键作用

所以,在创建链表的时候,首先应该先有一个Node型的头结点

Link::Link()
{   head = new Node();head->name = "头结点";head->next = head;head->prior = head;
}

在没有插入任何其他节点的时候,头结点既是头节点的上一节点也是头结点的下一节点,也就是所有指针全部指向自己。当然,这个头结点是private的,不可被外界访问到。有了头节点之后,我们可以开始对这个链表进行各种操作了。

class Link
{
private:Node * head;
public:Link();~Link();void traverse1();void traverse2();void insert(Node &n);void deleteByID(int id);void modifyByID(int id, string name);
};

这里,我只写了顺序遍历、倒序遍历、插入、按ID关键字删除、 按关键字修改信息五个函数。首先,我们先讲插入,没有插入就没有其他节点的存在。双循环链表的插入比较复杂一点,最好可以用纸笔进行模拟,可以促进理解。

插入,有两种情况,第一种是在除了头结点外为空的链上插入,此时我们要做的是将头节点的next指向要插入的节点(下面称为本节点),将头结点的prior指向本节点,将本节点的prior指向头结点,将本节点的next指向头结点。这样就可以实现头结点与本节点之间的双向循环。至于第二种,就是在链表已经足够长的情况下插入节点,我们这里的插入只是单纯插在尾部,至于其他类型的插入,大致不会差多少,所以谨以此例子作为示范。要插在尾部,我们要先找到尾部在哪里,于是有了一根指针(tail),tail=head,当tail的下一个节点不是头结点的时候,我们就继续找下一个,一直到他指向头结点,此时tail所代表的节点必然就是最后一个节点,new 出一个新的实体,这个实体的所有信息与传入的节点的信息完全一致,也就是进行拷贝,然后使newNode得next指向头结点,将头结点的prior指向newNode,将newNode的prior指向tail(最后一个节点),将最后一个节点的next指向newNode,这样,newNode就成为新的最后一个节点,并且是支持双向循环的。

void Link::insert(Node &n)
{Node *tail = head;if (head->next == head){Node *newNode = new Node(n);head->next = newNode;head->prior = newNode;newNode->prior = head;newNode->next = head;}else{while (tail->next != head){tail = tail->next;}Node *newNode = new Node(n);newNode->next = head;head->prior = newNode;newNode->prior = tail;tail->next = newNode;}
}

然后我们讲遍历,两种遍历的方法基本一致,只是顺序不一样而已,我只讲一种顺序遍历。首先创建一根副指针p,p指向头节点的下一个节点,然后将其输出出来,然后使p=p->next,实现指向再下一个节点,以循环不断做,直到p指向的是头结点才结束,这样就能够全部打印一遍。

void Link::traverse1()
{Node *p = head->next;while (p != head){p->display();p = p->next;}cout << endl;
}

然后是删除,按关键字删除,那么首先要做的就是找到关键字所在地方,建造两根副指针,p,q;p指向头结点的下一个节点,q指向本节点,以p查看每个节点信息,q作为他后面的节点,方便q的修改,当找到关键字后,先修改q的next,使其变成p的next,然后删除p节点,这样p所在位置的节点就被删除了,然后还需要做的是将原来p的next的节点的prior指向q,所以我们让p指向该节点,然后使他的prior指向q.这样就完成删除。(查找过程依然采用遍历查找)

void Link::deleteByID(int id)
{Node *p = head->next;Node *q = head;while (p != head){if (p->ID == id){q->next = p->next;delete(p);p = q->next;p->prior = q;}else{p = p->next;q = q->next;}}
}

最后讲的是按关键字修改。当然了,按关键字修改最重要的依然是找到关键字,所以重复同样的方法,创建副指针,循环查找,当找到关键字的时候,将信息修改成传入的信息即可。

void Link::modifyByID(int id, string name)
{Node *p = head->next;Node *q = head->prior;while (p != head){if (p->ID == id){p->name = name;}p = p->next;q = q->prior;}
}

最后补充说一下,查找的过程中可能遍历了整个链都找不到关键字,此时应该要报错或者有其他处理方法,我这里写的都是认为一定可以找到的,所以没有进行处理,有需要的还是要做一下处理。

那么双循环链表就讲到这里,如果有不同意见或者见解,又或者觉得我有哪里写错了,欢迎私聊我,大家一起学习一起进步,谢谢啦(QQ:2245440426)

完整代码如下

#include<iostream>
#include<string>
using namespace std;
/*######################################*/
/*            学生类双链表              */
/*######################################*/
class Node
{
public:int ID;string name;Node * next;Node * prior;Node() :next(nullptr), prior(nullptr) {}Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}void display(){cout << "ID:" << ID << endl << "Name:" << name << endl;}
};
class Link
{
private:Node * head;
public:Link();~Link();void traverse1();void traverse2();void insert(Node &n);void deleteByID(int id);void modifyByID(int id, string name);
};
Link::Link()
{   head = new Node();head->name = "头结点";head->next = head;head->prior = head;
}
Link::~Link()
{while (head != nullptr){Node *p = head->next;delete(head);head = p;}
}
void Link::traverse1()
{Node *p = head->next;while (p != head){p->display();p = p->next;}cout << endl;
}
void Link::traverse2()
{Node *p = head->prior;while (p != head){p->display();p = p->prior;}cout << endl;
}
void Link::insert(Node &n)
{Node *tail = head;if (head->next == head){Node *newNode = new Node(n);head->next = newNode;head->prior = newNode;newNode->prior = head;newNode->next = head;}else{while (tail->next != head){tail = tail->next;}Node *newNode = new Node(n);newNode->next = head;head->prior = newNode;newNode->prior = tail;tail->next = newNode;}
}
void Link::deleteByID(int id)
{Node *p = head->next;Node *q = head;while (p != head){if (p->ID == id){q->next = p->next;delete(p);p = q->next;p->prior = q;}else{p = p->next;q = q->next;}}
}
void Link::modifyByID(int id, string name)
{Node *p = head->next;Node *q = head->prior;while (p != head){if (p->ID == id){p->name = name;}p = p->next;q = q->prior;}
}
int main()
{Link link;Node s1(1001, "张三");Node s2(1002, "李四");Node s3(2001, "王五");link.insert(s1);link.insert(s2);link.insert(s3);link.traverse1();link.traverse2();link.deleteByID(s2.ID);link.traverse1();link.traverse2();link.modifyByID(s3.ID, "赵六");link.traverse1();link.traverse2();return 0;
}

双循环链表(C++)相关推荐

  1. 循环链表之双循环链表

    接着上一篇博文,把循环链表里的双循环链表的基本操纵按照我个人的理解进行总结一下. 依然沿袭过去的风格,进入双循环链表之前,先提另一种结构,双向链表,先有了双向链表再有了双循环链表.这两种结构和单链表一 ...

  2. 双链表及其他链式结构:双循环链表的创建算法(尾插法)

    请设计一个算法实现用尾插法创建一个双循环链表.调用已写好的函数printlist将该双循环链表中的元素从前向后打印一遍,然后再从后向前打印一遍. #include <stdio.h>#in ...

  3. 单循环,双向,双循环链表

    单向循环链表 相比于单向链表,单向循环链表仅仅是将单向链表的尾节点指向了头节点 #include <stdio.h> #include <stdlib.h>typedef st ...

  4. 链表(单/双/单循环/双循环)

    文中链接附上java版代码 1.单链表 单链表是一种链式存储的数据结构,方便插入/删除数据元素,对比数组,在进行插入删除等操作时,更节省空间.单链表中每一个结点的构成都是由数据元素+指针构成的 2.单 ...

  5. 不带头节点的链表有哪些缺点_23张图!万字详解「链表」,从小白到大佬!

    链表和数组是数据类型中两个重要又常用的基础数据类型. 数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解 ...

  6. 23张图!万字详解「链表」,从小白到大佬!

    链表和数组是数据类型中两个重要又常用的基础数据类型. 数组是连续存储在内存中的数据结构,因此它的优势是可以通过下标迅速的找到元素的位置,而它的缺点则是在插入和删除元素时会导致大量元素的被迫移动,为了解 ...

  7. linux内核数据结构之链表

    1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该代码来自linux内核,在li ...

  8. linux2.6内核链表

    一.        链表数据结构简介        链 表是一种常用的组织有序数据的数据结构,它通过指针将一系列数据节点连接成一条数据链,是线性表的一种重要实现方式.相对于数组,链表具有更好的动态性, ...

  9. linux内核链表分析

    一.常用的链表和内核链表的区别 1.1  常规链表结构        通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系.按照指针域的组织以及各个节 ...

最新文章

  1. pil ImportError: DLL load failed: 找不到指定的模块
  2. maven项目的依赖、继承管理
  3. java利用kafka生产消费消息
  4. Sqlite查询优化技巧——将LIKE语句转换为比较语句 -转
  5. 树和二叉树总结(三)—BST二叉排序树
  6. Html静态页面更新,解决浏览器缓存不更新问题
  7. android 炫酷图案解锁,16个超级漂亮的手机锁屏图案,炫酷到飞起,总有一款适合你...
  8. 采购供应链管理系统:企业采购与供应链管理更简单、快捷
  9. EF Code First 更新数据库, 数据库迁移
  10. 计算机专业报瑞士酒店管理,我适合去瑞士读酒店管理吗?
  11. 本周最新文献速递20210801
  12. linux服务器下如何显示中文的图片,Linux服务器中文显示问题
  13. Matlab图像处理入门教程(菜鸟级)
  14. 搜集计算机在各个领域的具体应用资料,计算机应用的毕业论文样本
  15. 安装ecshop php,PHPstudy和ecshop的安装和使用
  16. VMwareWorkStation虚拟机安装RHEL7.0Linux操作系统
  17. 003.小插曲之变量和字符串
  18. H.266/VVC代码学习21:帧内角度预测的实现 / 近对角模式的PDPC(xPredIntraAng)
  19. react-native使用高德地图获取当前地理位置
  20. 福州大学特大游戏制作团伙-冲刺日志(第9天11.23)

热门文章

  1. linux系统应用实验DNS的安装,DNS配置实验小结
  2. 学校举办朗诵比赛,邀请了 10 位评委为每一名参赛选手的表现打分。假设列表 lst_score
  3. 编译程序与解释程序的区别
  4. 【论文精读】resnet精读
  5. 从数据分析,看公司员工流失率分析报告
  6. pycrypto 和 lua-resty-rsa 进行跨语言的RSA加密解密.md
  7. python输入数字输出月份英文_编写一个程序,输入月份号,输出该月的中文名和英文名。...
  8. 数据可视化之数据的图表呈现
  9. 在windows配置Apache httpd代理服务器
  10. Mockplus原型交互跟我做之5 - 使用内容面板快速切换内容