目录
0基础概念
单向链表中,只有后驱指针,没有前驱指针
先将单向链表终端结点的指针端由空指针改为向头结点,使整个单链表形成一个环,这种头尾相接的单链表称为单循环链表,简称循环链表(circular linked list)
循环链表解决了一个问题:从任意一个结点开始,就可以访问所有的结点
单链表和循环链表的区别在于,判断链表是否结束的方式不一样
- 单链表判断链表结束的标志:next指针指向nullptr
- 循环链表判断链表结束的标志:next指针指向头指针
将头指针置换为尾指针
头结点初始化时候一定要注意
(图源:大话数据结构)
循环链表也是单向链表的衍生,所以也是遵循单向链表的基本特征的:
下图是单向链表的原理图:
为了更方便操作,链表都有头结点,且头结点不存储插入元素
只保存链表相关的性质变量,如链表长度,尾指针(队),或者前驱指针(双向链表)
而循环链表的数据关系如下:
收尾对接,从任意一个结点出发,都能够完成一周的循环遍历
链表的末尾结点的next指针,指向头结点。
具体操作代码如下:
1链表结构
1.1链表的基本内容(链表结点类型结构体):
//链表
struct CricularLink
{
CricularLink * next;
};
1.2头结点内容:
//链表性质
struct CricularLinkProperty
{
CricularLink header;
int size;
CricularLink * rear;//增加尾部指针
};
除了头结点中的头指针外(hearer.next) ,还有尾部指针和链表大小。
注意: 头结点,最好是CricularLink类型,因为头结点不存放任何的插入元素,只是在内存中占用一块地址就行
让整个链表有起始点,方便进行各类操作的产物,如果定义为CricularLink * 类型,这个指针无法指向任何内容,失去了指针的意义。
1.3用户数据结构(插入元素的数据结构):
typedef struct CricularLinkData
{
CricularLink node;
QString name;
int age;
}CLD;
用户自定义类型中,要求第一个成员必须是CricularLink类型(链表结点类型结构体变量 )
为了更好的开发和应用分离,就是面对对象编程
在开发过程中不知道用户会传递什么样的数据进来,为了让用户传递任何类型的数据进来,程序都能应对,采用如此办法。
下面通过一个例子对上述数据结构的设置做出解释:
.h
//链表节点数据类型
struct LinkContent
{
struct LinkContent * next;
};
//链表数据类型
struct LinkProperty
{
struct LinkContent Content;
int size;
};
typedef void * LinkListTwo;//为了更好的让用户理解函数
typedef struct Test
{
LinkContent node;
QString name;
int age;
}T;
数据结构和上述相同(只是部分变量名称进行了替换)
.cpp 具体操作如下:
T t1 = {nullptr,"1",10};
T t2 = {nullptr,"2",20};
T t3 = {nullptr,"3",30};
T t4 = {nullptr,"4",40};
T t5 = {nullptr,"5",50};
qDebug()<<"初始化结果:"<<t1.node.next;
qDebug()<<"初始化结果:"<<t2.node.next;
qDebug()<<"初始化结果:"<<t3.node.next;
qDebug()<<"初始化结果:"<<t4.node.next;
qDebug()<<"初始化结果:"<<t5.node.next;
首先是初始化,我们可以看见,链表中的next指针目前全部都被初始化为空了
qDebug()<<"t1首地址:"<<&t1;
qDebug()<<"t2首地址:"<<&t2;
qDebug()<<"t3首地址:"<<&t3;
qDebug()<<"t4首地址:"<<&t4;
qDebug()<<"t5首地址:"<<&t5;
LinkContent *myContent1 = (LinkContent*)(&t1);
LinkContent *myContent2 = (LinkContent*)(&t2);
LinkContent *myContent3 = (LinkContent*)(&t3);
LinkContent *myContent4 = (LinkContent*)(&t4);
LinkContent *myContent5 = (LinkContent*)(&t5);
myContent1->next = myContent2;
myContent2->next = myContent3;
myContent3->next = myContent4;
myContent4->next = myContent5;
myContent5->next = nullptr;
之后我们取出插入元素的首地址
并将其强制转换类型,转换为结构体第一个成员的类型
t1是T类型,取地址(&t1),变成了指针,指向结构体的第一个成员,也就是LinkContent类型的指针
这也就是为什么一定要强调把 链表结点类型结构体变量 定义在用户自定义结构体中第一位的原因
强转后,就可以访问到每个要插入数据的next指针了,然后直接进行赋值操作
之后进行输出,并对比输出内容
qDebug()<<"强转类型后t1.node.next:"<<t1.node.next;
qDebug()<<"强转类型后t2.node.next:"<<t2.node.next;
qDebug()<<"强转类型后t3.node.next:"<<t3.node.next;
qDebug()<<"强转类型后t4.node.next:"<<t4.node.next;
qDebug()<<"强转类型后t5.node.next:"<<t5.node.next;
可见,要插入元素之间的连接已经完成,那么以上只是演示
言外之意是,在开发者眼里,不管用户自定义类型有多少个成员变量,只要第一个成员变量是链表结点类型结构体变量,在开发者写各类接口的时候,直接将传入接口的参数强制转换成链表结点类型结构体变量即可,然后就可以完成链表元素之间的连接。
到时候只需要给用户提供:
- 链表结点类型结构体名称
- 头结点结构体名称(初始化和调用接口的时候用)
- 各个接口名称和输入要求
即可
做到了面向对象编程的基本要求
2初始化和销毁
typedef void * CLHeader;
为了让用户更好理解,增加可读性。
初始化过程中,需要先堆上要一部分内存空间
在new和malloc之间,选择原则很简单,需要扩容,用malloc,就像动态数组,及线性表的顺序存储,用malloc显然更好操作:
数据结构:线性表-动态数组:https://blog.csdn.net/qq_41605114/article/details/104315027
C/C++:细说new与malloc的10点区别 :https://blog.csdn.net/qq_41605114/article/details/104342587
//初始化
CLHeader Inite_CL()
{
//初始化
CricularLinkProperty * MyCl = new CricularLinkProperty;
MyCl->size = 0;
MyCl->header.next = &(MyCl->header);//循环链表 目前没有元素,所以自己指向自己
MyCl->rear = &(MyCl->header);//尾指针指向链表尾部,也就是目前的首地址
return MyCl;
}
进行初始化后,头结点中包含有头结点中的头指针外(hearer.next) ,还有尾部指针和链表大小。
大小自然为零,因为只有一个头结点,所以尾部指针指向头结点(MyCl->rear = &(MyCl->header);)
因为目前没有元素,所以头结点的头指针也指向自己(MyCl->header.next = &(MyCl->header);)
因为循环链表从如此,在单链表中,头结点的头指针指向nullptr
销毁也是一样的
//销毁
void Destroy_Cl(CLHeader Cl)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
delete MyCl;
}
new和delete对应,malloc和free对应,不用少释放,也不用多释放。彼此对应
3插入
链表的插入,整体上的思路都是找到要插入位置的上一个位置,进行插入操作,在循环链表和双向链表中,
因为有尾指针和前驱指针的存在,要考虑在尾部插入结点和在链表中插入结点两种情况
//插入
void Insert_Cl(CLHeader Cl,int place,void * data)
{
if(nullptr == Cl)
return;
if(nullptr == data)
return;
if(place <=0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = &(MyCl->header);
CricularLink * InsertData = (CricularLink *)data;
if(place > MyCl->size)//在链表尾部插入
{
qDebug()<<"在队尾插入";
MyCl->rear->next = InsertData;//1更新原来尾部原型的指向
InsertData->next = &(MyCl->header);//2断开原来尾部元素next的指向,将新插入元素的next指向头结点
MyCl->rear = InsertData;//3更新尾指针的指向
MyCl->size++;
}
else//在中间部分插入
{
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
InsertData->next = pCurrent->next;
pCurrent->next = InsertData;
MyCl->size++;
}
}
对其中尾部插入新结点进行说明:
具体程序如下:
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = &(MyCl->header);//头结点
CricularLink * InsertData = (CricularLink *)data;
if(place > MyCl->size)//在链表尾部插入
{
qDebug()<<"在队尾插入";
MyCl->rear->next = InsertData;//1更新原来尾部原型的指向
InsertData->next = &(MyCl->header);//2断开原来尾部元素next的指向,将新插入元素的next指向头结点
MyCl->rear = InsertData;//3更新尾指针的指向
MyCl->size++;
}
因为尾部指针一直指向尾部,所以直接调用即可,不用像 链表中插入结点那样,再去找位置。
在链表中插入结点
其他部分都是保护程序,插入部分的主要原理如上两图所示。
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = &(MyCl->header);//头结点
CricularLink * InsertData = (CricularLink *)data;
if(place > MyCl->size)//在链表尾部插入
{
.....
}
else//在中间部分插入
{
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
InsertData->next = pCurrent->next;
pCurrent->next = InsertData;
MyCl->size++;
}
链表的插入,整体上的思路都是找到要插入位置的上一个位置,PCurrent指向头结点,循环次数=插入位置-1
其中插入位置是这个元素插入后,在链表中的位置,所以是大于0的。
4遍历
普通遍历:没有什么好说的,单纯的循环输出即可
//遍历
void Foreach_Cl(CLHeader Cl,int SizeOFLoop,void(*Print)(void *))//第二个参数是遍历的遍数
{
if(nullptr == Cl)
return;
if(SizeOFLoop <= 0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;//只创建了一次性质,之后性质的MyCl.header就是指向链表的首地址,所以也可以把header改成hearder
CricularLink * pCurrent = MyCl->header.next;//第一个元素
for(int i = 0;i<SizeOFLoop;++i)
{
while(pCurrent!=&(MyCl->header))//next指针等于头指针时停止
{
Print(pCurrent);
pCurrent = pCurrent->next;
}
}
}
但是因为是循环链表,非常特殊,所以多了一种遍历方式,就是从任意位置开始,遍历一周
//从指定位置开始循环
void Arbitrary_Cl(CLHeader Cl,int place,int SizeOFLoop,void(*Print)(void *))
{
if(nullptr == Cl)
return;
if(place <= 0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = MyCl->header.next;//第一个元素
if(place > MyCl->size)
return;
//先找出place的元素
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
CricularLink * pStopFlag = pCurrent;//遍历的前一个元素
//再循环
for(int i = 0;i<SizeOFLoop;++i)
{
do
{
Print(pCurrent);
pCurrent = pCurrent->next;
if(pCurrent == &(MyCl->header))//头结点是空的,所以要跳过
{
pCurrent = pCurrent->next;
}
}while(pCurrent != pStopFlag);
}
}
唯一要注意的是,在路过头结点的时候,要跳过头结点,因为头结点什么都没有,不跳过的话会发生内存泄露的。
(上述遍历是从第一个结点开始的,所以算法和普通遍历不大一样)
因为开发过程中不知道用户输入的数据类型,所以输出部分需要用户写一个回调函数:
void PrintDATA(void * data)
{
if(nullptr == data)
return;
CLD * DATA = (CLD *)data;
qDebug()<<"DATA->age:"<<DATA->age<<"DATA->name:"<<DATA->name;
}
5删除
和插入一样,找到要删除元素的上一个位置
//按照位置删除
void Delete_Cl(CLHeader Cl,int place)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
if(place<=0)
return;
if(place>MyCl->size)
return;
CricularLink * pCurrent = &(MyCl->header);//从头开始
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
pCurrent->next = pCurrent->next->next;
MyCl->size--;//数量减去1
}
这时不用考虑链表尾部结点,删除对谁来说都一样,原理如下:
6链表拼接
拼接的原理也很简答,如下图所示,主要是操作尾指针
//合并
void Merge_Cl(CLHeader Cl1,CLHeader Cl2)
{
if(nullptr == Cl1)
return;
if(nullptr == Cl2)
return;
CricularLinkProperty * MyCl1 = (CricularLinkProperty * )Cl1;
CricularLinkProperty * MyCl2 = (CricularLinkProperty * )Cl2;
CricularLink * Cl2FristItem = MyCl2->header.next;
// 将Cl2拼接在Cl1后面
//1将cl1的末尾指针指向cl2的第一个元素
MyCl1->rear->next = Cl2FristItem;
//2将cl2的尾部最后一个元素的next指向cl1的头部
MyCl2->rear->next = &(MyCl1->header);
//最后,将cl1的指针指向cl2的尾部,也就是拼接后新链表的尾部
MyCl1->rear = MyCl2->rear;
//4个数的同一
MyCl1->size =MyCl1->size + MyCl2->size;
}
验证代码:
qDebug()<<"/*****************************进入循环链表部分*****************************/";
qDebug()<<"初始化";
CricularLinkProperty * MyCriclarLink = (CricularLinkProperty * )Inite_CL();
CLD cld1 = {nullptr,"111",1};
CLD cld2 = {nullptr,"222",2};
CLD cld3 = {nullptr,"333",3};
CLD cld4 = {nullptr,"444",4};
CLD cld5 = {nullptr,"555",5};
CLD cld6 = {nullptr,"666",6};
Insert_Cl(MyCriclarLink,1,&cld1);
Insert_Cl(MyCriclarLink,2,&cld2);
Insert_Cl(MyCriclarLink,3,&cld3);
Insert_Cl(MyCriclarLink,4,&cld4);
Insert_Cl(MyCriclarLink,5,&cld5);
Insert_Cl(MyCriclarLink,6,&cld6);
qDebug()<<"&cld1.node:"<<&cld1.node;
qDebug()<<"&cld2.node"<<&cld2.node<<"cld1.node.next:"<<cld1.node.next;
qDebug()<<"&cld3.node"<<&cld3.node<<"cld2.node.next"<<cld2.node.next;
qDebug()<<"&cld4.node"<<&cld4.node<<"cld3.node.next"<<cld3.node.next;
qDebug()<<"&cld5.node"<<&cld5.node<<"cld4.node.next"<<cld4.node.next;
qDebug()<<"&cld6.node"<<&cld6.node<<"cld5.node.next"<<cld5.node.next;
qDebug()<<"cld6.node.next"<<cld6.node.next<<"MyCriclarLink:"<<MyCriclarLink;
通过输出,可以看出插入操作是没有问题的
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"从4开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
qDebug()<<"删除第3个元素";
Delete_Cl(MyCriclarLink,3);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"删除第4个元素(通过按值查询)";
DeleteItem_Cl(MyCriclarLink,&cld4);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"将删除的3和4重新插入,插入到1,2的位置";
Insert_Cl(MyCriclarLink,1,&cld3);
Insert_Cl(MyCriclarLink,2,&cld4);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"从第4个元素开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
此部分程序验证了删除,按元素删除,和在不同位置插入
//链表2
CLD cld7 = {nullptr,"777",7};
CLD cld8 = {nullptr,"888",8};
CLD cld9 = {nullptr,"999",9};
CLD cld10 = {nullptr,"aaa",10};
CLD cld11= {nullptr,"bbb",11};
CLD cld12 = {nullptr,"ccc",12};
CricularLinkProperty * NewMyCriclarLink = (CricularLinkProperty * )Inite_CL();
Insert_Cl(NewMyCriclarLink,1,&cld7);
Insert_Cl(NewMyCriclarLink,2,&cld8);
Insert_Cl(NewMyCriclarLink,3,&cld9);
Insert_Cl(NewMyCriclarLink,4,&cld10);
Insert_Cl(NewMyCriclarLink,5,&cld11);
Insert_Cl(NewMyCriclarLink,6,&cld12);
qDebug()<<"NEW从头遍历";
Foreach_Cl(NewMyCriclarLink,1,PrintDATA);
创建链表2
qDebug()<<"链表拼接";
Merge_Cl(MyCriclarLink,NewMyCriclarLink);
qDebug()<<"从头遍历合并后的链表";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"合成后的个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"从第4个元素开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
查看链表是否合并成功
qDebug()<<"清空";
Clear_Cl(MyCriclarLink);
qDebug()<<"&cld1.node:"<<&cld1.node;
qDebug()<<"&cld2.node"<<&cld2.node<<"cld1.node.next:"<<cld1.node.next;
qDebug()<<"&cld3.node"<<&cld3.node<<"cld2.node.next"<<cld2.node.next;
qDebug()<<"&cld4.node"<<&cld4.node<<"cld3.node.next"<<cld3.node.next;
qDebug()<<"&cld5.node"<<&cld5.node<<"cld4.node.next"<<cld4.node.next;
qDebug()<<"&cld6.node"<<&cld6.node<<"cld5.node.next"<<cld5.node.next;
qDebug()<<"cld6.node.next"<<cld6.node.next<<"MyCriclarLink:"<<MyCriclarLink;
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"销毁";
Destroy_Cl(MyCriclarLink);
Destroy_Cl(NewMyCriclarLink);
验证清空功能
附录(全部代码)
.h
#ifndef CIRCULARLINK_H
#define CIRCULARLINK_H
#include <QWidget>
#include<QDebug>
class circularlink : public QWidget
{
Q_OBJECT
public:
explicit circularlink(QWidget *parent = nullptr);
signals:
public slots:
};
//链表
struct CricularLink
{
CricularLink * next;
};
//链表性质
struct CricularLinkProperty
{
CricularLink header;
//不能是指针,链表的第一个位置是空,不放任何元素,如果变成指针的话,指针无法指向头文件,因为头文件还没有被创建,逻辑上不合理
int size;
CricularLink * rear;//增加尾部指针
};
//包含数据的内容
typedef struct CricularLinkData
{
CricularLink node;
QString name;
int age;
}CLD;
typedef void * CLHeader;
//初始化
CLHeader Inite_CL();
//插入
void Insert_Cl(CLHeader Cl,int place,void * data);
//遍历
void Foreach_Cl(CLHeader Cl,int SizeOFLoop,void(*Print)(void *));//第二个参数是遍历的遍数
//任意位置循环遍历
void Arbitrary_Cl(CLHeader Cl,int place,int SizeOFLoop,void(*Print)(void *));
//打印函数
void PrintDATA(void * data);
//按照位置删除
void Delete_Cl(CLHeader Cl,int place);
//按照元素删除
void DeleteItem_Cl(CLHeader Cl,void * data);
//销毁
void Destroy_Cl(CLHeader Cl);
//清空
void Clear_Cl(CLHeader Cl);
//个数
int Size_Cl(CLHeader Cl);
//合并
void Merge_Cl(CLHeader Cl1,CLHeader Cl2);
#endif // CIRCULARLINK_H
.cpp
#include "circularlink.h"
circularlink::circularlink(QWidget *parent) : QWidget(parent)
{
qDebug()<<"/*****************************进入循环链表部分*****************************/";
qDebug()<<"初始化";
CricularLinkProperty * MyCriclarLink = (CricularLinkProperty * )Inite_CL();
CLD cld1 = {nullptr,"111",1};
CLD cld2 = {nullptr,"222",2};
CLD cld3 = {nullptr,"333",3};
CLD cld4 = {nullptr,"444",4};
CLD cld5 = {nullptr,"555",5};
CLD cld6 = {nullptr,"666",6};
Insert_Cl(MyCriclarLink,1,&cld1);
Insert_Cl(MyCriclarLink,2,&cld2);
Insert_Cl(MyCriclarLink,3,&cld3);
Insert_Cl(MyCriclarLink,4,&cld4);
Insert_Cl(MyCriclarLink,5,&cld5);
Insert_Cl(MyCriclarLink,6,&cld6);
qDebug()<<"&cld1.node:"<<&cld1.node;
qDebug()<<"&cld2.node"<<&cld2.node<<"cld1.node.next:"<<cld1.node.next;
qDebug()<<"&cld3.node"<<&cld3.node<<"cld2.node.next"<<cld2.node.next;
qDebug()<<"&cld4.node"<<&cld4.node<<"cld3.node.next"<<cld3.node.next;
qDebug()<<"&cld5.node"<<&cld5.node<<"cld4.node.next"<<cld4.node.next;
qDebug()<<"&cld6.node"<<&cld6.node<<"cld5.node.next"<<cld5.node.next;
qDebug()<<"cld6.node.next"<<cld6.node.next<<"MyCriclarLink:"<<MyCriclarLink;
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"从4开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
qDebug()<<"删除第3个元素";
Delete_Cl(MyCriclarLink,3);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"删除第4个元素(通过按值查询)";
DeleteItem_Cl(MyCriclarLink,&cld4);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"将删除的3和4重新插入,插入到1,2的位置";
Insert_Cl(MyCriclarLink,1,&cld3);
Insert_Cl(MyCriclarLink,2,&cld4);
qDebug()<<"从头遍历";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"从第4个元素开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
//链表2
CLD cld7 = {nullptr,"777",7};
CLD cld8 = {nullptr,"888",8};
CLD cld9 = {nullptr,"999",9};
CLD cld10 = {nullptr,"aaa",10};
CLD cld11= {nullptr,"bbb",11};
CLD cld12 = {nullptr,"ccc",12};
CricularLinkProperty * NewMyCriclarLink = (CricularLinkProperty * )Inite_CL();
Insert_Cl(NewMyCriclarLink,1,&cld7);
Insert_Cl(NewMyCriclarLink,2,&cld8);
Insert_Cl(NewMyCriclarLink,3,&cld9);
Insert_Cl(NewMyCriclarLink,4,&cld10);
Insert_Cl(NewMyCriclarLink,5,&cld11);
Insert_Cl(NewMyCriclarLink,6,&cld12);
qDebug()<<"NEW从头遍历";
Foreach_Cl(NewMyCriclarLink,1,PrintDATA);
qDebug()<<"链表拼接";
Merge_Cl(MyCriclarLink,NewMyCriclarLink);
qDebug()<<"从头遍历合并后的链表";
Foreach_Cl(MyCriclarLink,1,PrintDATA);
qDebug()<<"合成后的个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"从第4个元素开始遍历";
Arbitrary_Cl(MyCriclarLink,4,1,PrintDATA);
qDebug()<<"清空";
Clear_Cl(MyCriclarLink);
qDebug()<<"&cld1.node:"<<&cld1.node;
qDebug()<<"&cld2.node"<<&cld2.node<<"cld1.node.next:"<<cld1.node.next;
qDebug()<<"&cld3.node"<<&cld3.node<<"cld2.node.next"<<cld2.node.next;
qDebug()<<"&cld4.node"<<&cld4.node<<"cld3.node.next"<<cld3.node.next;
qDebug()<<"&cld5.node"<<&cld5.node<<"cld4.node.next"<<cld4.node.next;
qDebug()<<"&cld6.node"<<&cld6.node<<"cld5.node.next"<<cld5.node.next;
qDebug()<<"cld6.node.next"<<cld6.node.next<<"MyCriclarLink:"<<MyCriclarLink;
qDebug()<<"个数"<<Size_Cl(MyCriclarLink);
qDebug()<<"销毁";
Destroy_Cl(MyCriclarLink);
Destroy_Cl(NewMyCriclarLink);
}
//初始化
CLHeader Inite_CL()
{
//初始化
CricularLinkProperty * MyCl = new CricularLinkProperty;
MyCl->size = 0;
MyCl->header.next = &(MyCl->header);//循环链表 目前没有元素,所以自己指向自己
MyCl->rear = &(MyCl->header);//尾指针指向链表尾部,也就是目前的首地址
return MyCl;
}
//插入
void Insert_Cl(CLHeader Cl,int place,void * data)
{
if(nullptr == Cl)
return;
if(nullptr == data)
return;
if(place <=0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = &(MyCl->header);//头结点
CricularLink * InsertData = (CricularLink *)data;
if(place > MyCl->size)//在链表尾部插入
{
qDebug()<<"在队尾插入";
MyCl->rear->next = InsertData;//1更新原来尾部原型的指向
InsertData->next = &(MyCl->header);//2断开原来尾部元素next的指向,将新插入元素的next指向头结点
MyCl->rear = InsertData;//3更新尾指针的指向
MyCl->size++;
}
else//在中间部分插入
{
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
InsertData->next = pCurrent->next;
pCurrent->next = InsertData;
MyCl->size++;
}
}
//遍历
void Foreach_Cl(CLHeader Cl,int SizeOFLoop,void(*Print)(void *))//第二个参数是遍历的遍数
{
if(nullptr == Cl)
return;
if(SizeOFLoop <= 0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;//只创建了一次性质,之后性质的MyCl.header就是指向链表的首地址,所以也可以把header改成hearder
CricularLink * pCurrent = MyCl->header.next;//第一个元素
for(int i = 0;i<SizeOFLoop;++i)
{
while(pCurrent!=&(MyCl->header))//next指针等于头指针时停止
{
Print(pCurrent);
pCurrent = pCurrent->next;
}
}
}
//从指定位置开始循环
void Arbitrary_Cl(CLHeader Cl,int place,int SizeOFLoop,void(*Print)(void *))
{
if(nullptr == Cl)
return;
if(place <= 0)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = MyCl->header.next;//第一个元素
if(place > MyCl->size)
return;
//先找出place的元素
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
CricularLink * pStopFlag = pCurrent;//第一个元素
//再循环
for(int i = 0;i<SizeOFLoop;++i)
{
do
{
Print(pCurrent);
pCurrent = pCurrent->next;
if(pCurrent == &(MyCl->header))//第一个元素是空的,所以要跳过第一个元素
{
pCurrent = pCurrent->next;
}
}while(pCurrent != pStopFlag);
}
}
void PrintDATA(void * data)
{
if(nullptr == data)
return;
CLD * DATA = (CLD *)data;
qDebug()<<"DATA->age:"<<DATA->age<<"DATA->name:"<<DATA->name;
}
//按照位置删除
void Delete_Cl(CLHeader Cl,int place)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
if(place<=0)
return;
if(place>MyCl->size)
return;
CricularLink * pCurrent = &(MyCl->header);//从头开始
for(int i = 0;i<place-1;++i)
{
pCurrent = pCurrent->next;
}
pCurrent->next = pCurrent->next->next;
MyCl->size--;//数量减去1
}
//按照元素删除
void DeleteItem_Cl(CLHeader Cl,void * data)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * InsertItem = (CricularLink *)data;
CricularLink * pCurrent = MyCl->header.next;//从第一个元素开始
int place = 1;//注意起始点
for(int i = 0;i<MyCl->size;++i)
{
if(pCurrent == InsertItem)
{
Delete_Cl(Cl,place);//按照位置删除
break;
}
pCurrent = pCurrent->next;
place++;
}
}
//销毁
void Destroy_Cl(CLHeader Cl)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
delete MyCl;
}
//清空
void Clear_Cl(CLHeader Cl)
{
if(nullptr == Cl)
return;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
CricularLink * pCurrent = MyCl->header.next;//第一个元素
CricularLink * pDel = nullptr;//
while(pCurrent!= &(MyCl->header))//指向头文件说明循环结束
{
pDel = pCurrent;
pCurrent = pCurrent->next;
pDel->next = nullptr;//指向空
}//顺序不能错
MyCl->rear = &(MyCl->header);//因为清空,尾指针自然指向头指针
MyCl->header.next = &(MyCl->header);//头指针的next指向自己,因为只剩自己一个了
MyCl->size = 0;//清空,数据只剩0了
}
//个数
int Size_Cl(CLHeader Cl)
{
if(nullptr == Cl)
return 0;
CricularLinkProperty * MyCl = (CricularLinkProperty * )Cl;
return MyCl->size;
}
//合并
void Merge_Cl(CLHeader Cl1,CLHeader Cl2)
{
if(nullptr == Cl1)
return;
if(nullptr == Cl2)
return;
CricularLinkProperty * MyCl1 = (CricularLinkProperty * )Cl1;
CricularLinkProperty * MyCl2 = (CricularLinkProperty * )Cl2;
CricularLink * Cl2FristItem = MyCl2->header.next;
// 将Cl2拼接在Cl1后面
//1将cl1的末尾指针指向cl2的第一个元素
MyCl1->rear->next = Cl2FristItem;
//2将cl2的尾部最后一个元素的next指向cl1的头部
MyCl2->rear->next = &(MyCl1->header);
//最后,将cl1的指针指向cl2的尾部,也就是拼接后新链表的尾部
MyCl1->rear = MyCl2->rear;
//4个数的同一
MyCl1->size =MyCl1->size + MyCl2->size;
}