目录
2.3线性表的链式存储(单向链表)
整个数据结构中,很重要的一点是,初始化和销毁对应,插入和删除对应,有malloc有free,一定要对应。
2.3.1线性表的链式存储(单向链表)的设计与实现
初始化
单向链表的结构不同于动态数组,是分散的,数据和数据之间是靠指针来衔接的。
//链表节点数据类型
struct LinkContent
{
void * data;
struct LinkContent * next;
};
//链表数据类型
struct LinkProperty
{
struct LinkContent Content;
int size;
};
分两个结构体,第一个结构体是LinkContent,链表内容结构体 链表具体内容:
- 指向所存数据的万能指针
- 指向自身类型的指针(目的是为了指向下一个结构体)
第二个结构体是Linkproperty ,链表性质结构体 ,包含
- 链表具体内容的结构体对象
- 链表目前的大小
为了方便可读性,进行了如下操作:
typedef void * LinkList;//为了更好的让用户理解函数
具体的初始化函数
.h
//初始化链表
LinkList Init_Linklist();
.cpp
//初始化链表
LinkList Init_Linklist()
{
LinkProperty * List = (LinkProperty *)malloc(sizeof(LinkProperty));
if(nullptr == List)
{
return nullptr;
}
List->Content.data = nullptr;
List->Content.next=nullptr;
List->size = 0;
return List;
}
创建Linkproperty ,链表性质结构体
分配空间(此处使用new也可以),将链表内容(List.Content)中的数据指向空,将链表内容(List.Content)中的next指针也指向空。
Linkproperty中的大小(size)自然是零。
插入
.h
//插入节点
void Insert_LinkList(LinkList list,int pose,void * data);
输入参数要求:(void *类型)链表属性,元素在插入该元素插入后,新的数据中的位置,要插入的数据
.cpp
//插入节点
void Insert_LinkList(LinkList list,int pose,void * data)//规定插入原则,pose是插入该数据后,该数据的全新的数据中的位置
{
if(nullptr == list)
return;
if(nullptr == data)
return;
LinkProperty * mylist = (LinkProperty * )list;
if(pose<=0)
{
return;
}
if(pose>mylist->size)
{
pose = mylist->size+1;
}
//需要找到插入节点的前一个位置
LinkContent * pCurrent = &(mylist->Content);//链表本身头文件所指向的位置
for(int i = 0;i<pose-1;++i)//找到要插入的前一个位置
{
pCurrent = pCurrent->next;//找到pose前一个位置
}
//创建新节点,为要插入的内容开辟空间
LinkContent * newnode = (LinkContent*)malloc(sizeof(LinkContent));
newnode->data = data;
newnode->next = nullptr;
//新节点插入
newnode->next = pCurrent->next;
pCurrent->next = newnode;
++mylist->size;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
LinkProperty * mylist = (LinkProperty * )list;
对输入的插入位置进行判断和筛选,插入位置小于等于0,直接返回
插入位置大于元素个数,及目前有5个元素,但是要在第100个位置插入第6个,程序自动将这个元素放在位置6上,而不是真的要放在位置100上放置该元素
正式插入的过程如下图所示:
需要找到插入位置的前一个元素,将其next指针指向要插入的新元素,而新元素的next指针指向原来该位置上的元素
//需要找到插入节点的前一个位置
LinkContent * pCurrent = &(mylist->Content);//链表本身头文件所指向的位置
for(int i = 0;i<pose-1;++i)//找到要插入的前一个位置
{
pCurrent = pCurrent->next;//找到pose前一个位置
}
需要创建一个LinkContent内容变量,将链表的头文件的位置确定
之后进行循环,找到插入位置的前一个元素,所以pose-1
每次循环都将进行递进操作,将下一个元素的指针给现在的元素
之后创建新的一个LinkContent内容变量,将要插入的元素放入这个变量储存
//创建新节点,为要插入的内容开辟空间
LinkContent * newnode = (LinkContent*)malloc(sizeof(LinkContent));
newnode->data = data;
newnode->next = nullptr;
需要开辟空间,保存的内容直接等于输入的参数
之后就是指针的断开和交接了,正如上面示意图所示:
//新节点插入
newnode->next = pCurrent->next;
pCurrent->next = newnode;
PCurrent->next 指向的是原来该位置的元素,现在是插入新元素的下一个元素,所以要让newnode->next = pCurrent->next
(下图的橘黄色点划线断开,重新连接黄色的线)
之后将新插入的元素的地址给上一个元素的next即可
(PCurrent->next 原本是0XAAA,现在要变成0X???,也就是程序中的newnode)
不同于动态数组,链表的插入也是很奇妙的,动态数组数据是连续储存的而且之间没有空数据
但是按照链表的这种插入模式,插入第一个元素时,pose是1,不进入循环,直接
newnode->next = pCurrent->next;
pCurrent->next = newnode;
所以,单向链表的第一个,实际上的第一个元素,void * data是空,其间什么也没有
遍历
.h
//遍历
void Foreach_LinkList(LinkList list,void(*FOREACH)(void *));
void FOREACH(void * data);
输入参数要求:(void *类型)链表属性,回调函数
解释: 回调函数在此处出现的意思,因为输入数据的类型,在开发者眼里是未知的,所以输出交给用户去写。
.cpp
//遍历
void Foreach_LinkList(LinkList list,void(*FOREACH)(void *))
{
if(nullptr==list)
return;
if(nullptr == FOREACH)
return;
LinkProperty *mylist = (LinkProperty*)list;
LinkContent *pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
FOREACH(pCurrent->data);
pCurrent = pCurrent->next;
}
}
void FOREACH(void * data)
{
OWL_PS * P_data = (OWL_PS *)data;
qDebug()<<"P_data->age:"<<P_data->age<<endl
<<"P_data->name:"<<P_data->name;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
因为单向链表的特性,及的一个元素是空,所以才有以下的操作
LinkContent *pCurrent = mylist->Content.next;
从头(mylist->Content)的下一个(mylist->Content.next)开始遍历
删除
按照位置删除
.h
//按照位置删除
void RemoveByPlace_LinkList(LinkList list,int pose);
输入参数要求:(void *类型)链表属性,要删除内容的位置
.cpp
//按照位置删除
void RemoveByPlace_LinkList(LinkList list,int pose)
{
if(nullptr == list)
return;
if(pose<=0||pose>mylist->size)
{
return;
}
//找位置
LinkProperty *mylist = (LinkProperty*)list;
LinkContent *pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
//先保存待删除节点
LinkContent *pDEl = pCurrent->next;
//重新建立待删除节点的前驱和后续节点关系
pCurrent->next = pDEl->next;
free(pDEl);
pDEl = nullptr;
--mylist->size;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
删除特定元素,示意图如上,还是要扎到要移除元素的上一个元素,然后重新规定next指针的纸箱,并将要移除的内容free掉
按照值删除
.h
//按照值删除
void RemoveByData_LinkList(LinkList list,void * data,int(*CPMPARE)(void *,void *));
输入参数要求:(void *类型)链表属性,要删除的内容,回调函数
解释: 回调函数在此处出现的意思,因为输入数据的类型,在开发者眼里是未知的,所以输出交给用户去写。
.cpp
//按照值删除
void RemoveByData_LinkList(LinkList list,void * data,int(*CPMPARE)(void *,void *))
{
if(nullptr == list)
return;
if(nullptr == data)
return;
if(nullptr == CPMPARE)
return;
LinkProperty * mylist = (LinkProperty *)list;
//辅助指针变量
LinkContent * pPrev = &(mylist->Content);//指向最开始
LinkContent * pPcurrent = pPrev->next;//指向最开始的下一个
//pPcurrent指向的就是要删除的内容
while(pPcurrent!=nullptr)
{
if(CPMPARE(pPcurrent->data,data))
{
//找到了
pPrev->next = pPcurrent->next;
free(pPcurrent);
break;
}
pPrev = pPcurrent;
pPcurrent = pPcurrent->next;
}
--mylist->size;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
按照值删除比较简单,不再解释
清空
.h
//清空list
void Clear_LinkList(LinkList list);
输入参数要求:(void *类型)链表属性
.cpp
//清空list
void Clear_LinkList(LinkList list)
{
if(nullptr == list)
return;
//辅助指针变量
LinkProperty * mylist = (LinkProperty *)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
//先缓存下一个节点的地址
LinkContent * PNext = pCurrent->next;
free(pCurrent);
pCurrent = PNext;
}
mylist->Content.next=nullptr;
mylist->size = 0;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
因为单向链表的特性,及的一个元素是空,所以才有以下的操作
LinkContent *pCurrent = mylist->Content.next;
从头(mylist->Content)的下一个(mylist->Content.next)开始遍历
大小
.h
//大小
int Size_LinkList(LinkList list)
输入参数要求:(void *类型)链表属性,要删除的内容,回调函数
.cpp
//大小
int Size_LinkList(LinkList list)
{
if(nullptr == list)
return 0;
LinkProperty * mylist = (LinkProperty *)list;
return mylist->size;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
简单的输出
销毁
.h
//销毁
void Destroy_LinkList(LinkList list);
输入参数要求:(void *类型)链表属性,要删除的内容,回调函数
.cpp
//销毁
void Destroy_LinkList(LinkList list)
{
if(nullptr == list)
return;
//辅助指针变量
LinkProperty * mylist = (LinkProperty *)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
//先缓存下一个节点的地址
LinkContent * PNext = pCurrent->next;
free(pCurrent);
pCurrent = PNext;
}
mylist->Content.next=nullptr;
mylist->size = 0;
free(list);
list = nullptr;
}
输入的参数是void *类型,在此程序中是用LinkList代替,为了更好的理解
调用插入函数,第一个参数要传递Linkproperty类型的地址
第一步,先将类型进行转换,之后好进行其他操作
因为单向链表的特性,及的一个元素是空,所以才有以下的操作
LinkContent *pCurrent = mylist->Content.next;
从头(mylist->Content)的下一个(mylist->Content.next)开始遍历
free掉有实际data的内容后,list本身就指向第一个元素,及data是空的内容,之后再free掉即可。
示例:
现在进行如下操作
链表中储存的数据类型:
.h
typedef struct OWL_Person
{
int age;
QString name;
}OWL_PS;
.cpp
OWL_PS p1 = {1,"aaa"};
OWL_PS p2 = {2,"bbb"};
OWL_PS p3 = {3,"ccc"};
OWL_PS p4 = {4,"ddd"};
OWL_PS p5 = {5,"eee"};
LinkList list = Init_Linklist();
Insert_LinkList(list,1,&p1);
Insert_LinkList(list,1,&p2);
Insert_LinkList(list,1,&p3);
Insert_LinkList(list,2,&p4);
Insert_LinkList(list,3,&p5);
//3 4 5 2 1
Insert_LinkList(list,6,&p1);
//34521
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"删除aaa";
RemoveByData_LinkList(list,&p1,CPMPARE);
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//CPMPARE
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"移除位置3";
RemoveByPlace_LinkList(list,3);
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//CPMPARE
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"清空";
Clear_LinkList(list);
qDebug()<<"数量"<<Size_LinkList(list);
LinkProperty * Painter = (LinkProperty *)list;
qDebug()<<Painter->Content.data;
qDebug()<<"销毁";
Destroy_LinkList(list);
输出内容:
从输出可以看出,链表的第一个元素的data是空
附录:
完整代码:
.h
#ifndef ONEWAYLIST_H
#define ONEWAYLIST_H
#include <QWidget>
#include<QDebug>
//链表节点数据类型
struct LinkContent
{
void * data;
struct LinkContent * next;
};
//链表数据类型
struct LinkProperty
{
struct LinkContent Content;
int size;
};
typedef struct OWL_Person
{
int age;
QString name;
}OWL_PS;
typedef void * LinkList;//为了更好的让用户理解函数
//初始化链表
LinkList Init_Linklist();
//插入节点
void Insert_LinkList(LinkList list,int pose,void * data);
//遍历
void Foreach_LinkList(LinkList list,void(*FOREACH)(void *));
//按照位置删除
void RemoveByPlace_LinkList(LinkList list,int pose);
//按照值删除
void RemoveByData_LinkList(LinkList list,void * data,int(*CPMPARE)(void *,void *));
//清空list
void Clear_LinkList(LinkList list);
//大小
int Size_LinkList(LinkList list);
//销毁
void Destroy_LinkList(LinkList list);
//回调函数
void FOREACH(void * data);
int CPMPARE(void *data1,void *data2);
class OneWayList : public QWidget
{
Q_OBJECT
public:
explicit OneWayList(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // ONEWAYLIST_H
.cpp
#include "onewaylist.h"
OneWayList::OneWayList(QWidget *parent) : QWidget(parent)
{
OWL_PS p1 = {1,"aaa"};
OWL_PS p2 = {2,"bbb"};
OWL_PS p3 = {3,"ccc"};
OWL_PS p4 = {4,"ddd"};
OWL_PS p5 = {5,"eee"};
LinkList list = Init_Linklist();
Insert_LinkList(list,1,&p1);
Insert_LinkList(list,1,&p2);
Insert_LinkList(list,1,&p3);
Insert_LinkList(list,2,&p4);
Insert_LinkList(list,3,&p5);
//3 4 5 2 1
Insert_LinkList(list,6,&p1);
//34521
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"删除aaa";
RemoveByData_LinkList(list,&p1,CPMPARE);
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//CPMPARE
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"移除位置3";
RemoveByPlace_LinkList(list,3);
qDebug()<<"遍历";
Foreach_LinkList(list,FOREACH);//CPMPARE
qDebug()<<"数量"<<Size_LinkList(list);
qDebug()<<"清空";
Clear_LinkList(list);
qDebug()<<"数量"<<Size_LinkList(list);
LinkProperty * Painter = (LinkProperty *)list;
qDebug()<<Painter->Content.data;
qDebug()<<"销毁";
Destroy_LinkList(list);
}
//初始化链表
LinkList Init_Linklist()
{
LinkProperty * List = (LinkProperty *)malloc(sizeof(LinkProperty));
if(nullptr == List)
{
return nullptr;
}
List->Content.data = nullptr;
List->Content.next=nullptr;
List->size = 0;
return List;
}
//插入节点
void Insert_LinkList(LinkList list,int pose,void * data)//规定插入原则,pose是插入该数据后,该数据的全新的数据中的位置
{
if(nullptr == list)
return;
if(nullptr == data)
return;
LinkProperty * mylist = (LinkProperty * )list;
if(pose<=0)
{
return;
}
if(pose>mylist->size)
{
pose = mylist->size+1;
}
//需要找到插入节点的前一个位置
LinkContent * pCurrent = &(mylist->Content);//链表本身头文件所指向的位置
for(int i = 0;i<pose-1;++i)//找到要插入的前一个位置
{
pCurrent = pCurrent->next;//找到pose前一个位置
}
//创建新节点,为要插入的内容开辟空间
LinkContent * newnode = (LinkContent*)malloc(sizeof(LinkContent));
newnode->data = data;
newnode->next = nullptr;
//新节点插入
newnode->next = pCurrent->next;
pCurrent->next = newnode;
++mylist->size;
}
//遍历
void Foreach_LinkList(LinkList list,void(*FOREACH)(void *))
{
if(nullptr==list)
return;
if(nullptr == FOREACH)
return;
LinkProperty *mylist = (LinkProperty*)list;
LinkContent *pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
FOREACH(pCurrent->data);
pCurrent = pCurrent->next;
}
}
void FOREACH(void * data)
{
OWL_PS * P_data = (OWL_PS *)data;
qDebug()<<"P_data->age:"<<P_data->age<<endl
<<"P_data->name:"<<P_data->name;
}
//按照位置删除
void RemoveByPlace_LinkList(LinkList list,int pose)
{
if(nullptr == list)
return;
LinkProperty *mylist = (LinkProperty*)list;
if(pose<=0||pose>mylist->size)
{
return;
}
//找位置
LinkContent *pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
//先保存待删除节点
LinkContent *pDEl = pCurrent->next;
//重新建立待删除节点的前驱和后续节点关系
pCurrent->next = pDEl->next;
free(pDEl);
pDEl = nullptr;
--mylist->size;
}
//按照值删除
void RemoveByData_LinkList(LinkList list,void * data,int(*CPMPARE)(void *,void *))
{
if(nullptr == list)
return;
if(nullptr == data)
return;
if(nullptr == CPMPARE)
return;
LinkProperty * mylist = (LinkProperty *)list;
//辅助指针变量
LinkContent * pPrev = &(mylist->Content);//指向最开始
LinkContent * pPcurrent = pPrev->next;//指向最开始的下一个
//pPcurrent指向的就是要删除的内容
while(pPcurrent!=nullptr)
{
if(CPMPARE(pPcurrent->data,data))
{
//找到了
pPrev->next = pPcurrent->next;
free(pPcurrent);
break;
}
pPrev = pPcurrent;
pPcurrent = pPcurrent->next;
}
--mylist->size;
}
int CPMPARE(void *data1,void *data2)
{
OWL_PS * DataStruct1 = (OWL_PS *)data1;
OWL_PS * DataStruct2 = (OWL_PS *)data2;
return ( DataStruct1->name==DataStruct2->name ) && ( DataStruct1->age == DataStruct2->age );
}
//清空list
void Clear_LinkList(LinkList list)
{
if(nullptr == list)
return;
//辅助指针变量
LinkProperty * mylist = (LinkProperty *)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
//先缓存下一个节点的地址
LinkContent * PNext = pCurrent->next;
free(pCurrent);
pCurrent = PNext;
}
mylist->Content.next=nullptr;
mylist->size = 0;
}
//大小
int Size_LinkList(LinkList list)
{
if(nullptr == list)
return 0;
LinkProperty * mylist = (LinkProperty *)list;
return mylist->size;
}
//销毁
void Destroy_LinkList(LinkList list)
{
if(nullptr == list)
return;
//辅助指针变量
LinkProperty * mylist = (LinkProperty *)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent!=nullptr)
{
//先缓存下一个节点的地址
LinkContent * PNext = pCurrent->next;
free(pCurrent);
pCurrent = PNext;
}
mylist->Content.next=nullptr;
mylist->size = 0;
free(list);
list = nullptr;
}
现在对以上结构进行改写,将数据与链表中最重要的,也就是指向下一个数据结构的指针,融入其中,如下图所示:
.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;
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;
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;
现在对以上内容进行介绍:此部分也是优化版本链表的原理
首先是赋值操作,并且进行了一系列的输出:
之后,进行了如下操作:
LinkContent *myContent1 = (LinkContent*)(&t1);
LinkContent *myContent2 = (LinkContent*)(&t2);
t1是T类型,取地址(&t1),变成了指针,指向结构体的第一个元素,也就是LinkContent类型的指针
直接强制转换类型,变成LinkContent类型
之后进行如下操作:
myContent1->next = myContent2;
将第一个T的对象,中的linkContent node的成员next指针,指向myContent2,也就是(&t2)
如此,整个结构如图所示:
而且强制转换过程中,不会干扰到用户的数据,因为开发者是不知道的,和之前的版本一样,意义都在于面对对象编程,开发和使用完全分类,数据的隐藏很重要,其他的接口程序都大同小异,不在进行具体的解释
整体输出:
可以看出,已经完成了链表的基本要求。
下面是整个程序段:
初始化
//初始化链表
LinkListTwo Init_LinklistTwo()
{
LinkProperty * list = (LinkProperty * )malloc(sizeof(LinkProperty));
if(nullptr == list)
{
return nullptr;
}
list->size = 0;
list->Content.next = nullptr;
return list;
}
插入
//插入节点
void Insert_LinklistTwo(LinkListTwo list,int pose,void * data)
{
if(nullptr == list)
return;
if(nullptr == data)
return;
if(pose<=0)
return;
LinkProperty * mylist = (LinkProperty*)list;
LinkContent *myContent = (LinkContent*)data;
if(pose>mylist->size)
{
pose = mylist->size+1;
}
LinkContent * pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
myContent->next = pCurrent->next;
pCurrent->next = myContent;
++mylist->size;
}
遍历
//遍历
void Foreach_LinklistTwo(LinkListTwo list,void(*FOREACH)(void *))
{
if(nullptr == list)
return;
if(nullptr == FOREACH)
return;
LinkProperty * mylist = (LinkProperty*)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent != nullptr)
{
FOREACH(pCurrent);
pCurrent = pCurrent->next;
}
}
void myPrint_LinklistTwo(void * item)
{
T * data = (T*)item;
qDebug()<<"name:"<<data->name<<" size:"<<data->age;
}
删除
void RemoveBypos_LinklistTwo(LinkListTwo list,int pose)
{
if(nullptr == list)
return;
LinkProperty * mylist = (LinkProperty*)list;
if(pose<0||pose>mylist->size)
return;
LinkContent * pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
LinkContent * pDel = pCurrent->next;
pCurrent->next = pDel->next;
--mylist->size;
}
销毁
void Destroy_LinklistTwo(LinkListTwo list)
{
if(nullptr == list)
return;
free(list);
list = nullptr;
}
进行如下操作:
LinkProperty * list = (LinkProperty*)Init_LinklistTwo();
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:"<<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;
qDebug()<<"插入";
Insert_LinklistTwo(list,1,&t1);
Insert_LinklistTwo(list,2,&t2);
Insert_LinklistTwo(list,3,&t3);
Insert_LinklistTwo(list,4,&t4);
Insert_LinklistTwo(list,5,&t5);
qDebug()<<"遍历";
Foreach_LinklistTwo(list,myPrint_LinklistTwo);
qDebug()<<"删除";
RemoveBypos_LinklistTwo(list,1);
qDebug()<<"遍历";
Foreach_LinklistTwo(list,myPrint_LinklistTwo);
qDebug()<<"销毁";
Destroy_LinklistTwo(list);
输出:
完整代码:
#ifndef ONEWAYLISTTWO_H
#define ONEWAYLISTTWO_H
#include <QWidget>
#include<QDebug>
//链表节点数据类型
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;
//初始化链表
LinkListTwo Init_LinklistTwo();
//插入节点
void Insert_LinklistTwo(LinkListTwo list,int pose,void * data);
//遍历
void Foreach_LinklistTwo(LinkListTwo list,void(*FOREACH)(void *));
//删除
void RemoveBypos_LinklistTwo(LinkListTwo list,int pose);
//销毁
void Destroy_LinklistTwo(LinkListTwo list);
//回调函数
void myPrint_LinklistTwo(void * item);
class onewaylisttwo : public QWidget
{
Q_OBJECT
public:
explicit onewaylisttwo(QWidget *parent = nullptr);
signals:
public slots:
};
#endif // ONEWAYLISTTWO_H
#include "onewaylisttwo.h"
onewaylisttwo::onewaylisttwo(QWidget *parent) : QWidget(parent)
{
LinkProperty * list = (LinkProperty*)Init_LinklistTwo();
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:"<<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;
qDebug()<<"插入";
Insert_LinklistTwo(list,1,&t1);
Insert_LinklistTwo(list,2,&t2);
Insert_LinklistTwo(list,3,&t3);
Insert_LinklistTwo(list,4,&t4);
Insert_LinklistTwo(list,5,&t5);
qDebug()<<"遍历";
Foreach_LinklistTwo(list,myPrint_LinklistTwo);
qDebug()<<"删除";
RemoveBypos_LinklistTwo(list,1);
qDebug()<<"遍历";
Foreach_LinklistTwo(list,myPrint_LinklistTwo);
qDebug()<<"销毁";
Destroy_LinklistTwo(list);
}
//初始化链表
LinkListTwo Init_LinklistTwo()
{
LinkProperty * list = (LinkProperty * )malloc(sizeof(LinkProperty));
if(nullptr == list)
{
return nullptr;
}
list->size = 0;
list->Content.next = nullptr;
return list;
}
//插入节点
void Insert_LinklistTwo(LinkListTwo list,int pose,void * data)
{
if(nullptr == list)
return;
if(nullptr == data)
return;
if(pose<=0)
return;
LinkProperty * mylist = (LinkProperty*)list;
LinkContent *myContent = (LinkContent*)data;
if(pose>mylist->size)
{
pose = mylist->size+1;
}
LinkContent * pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
myContent->next = pCurrent->next;
pCurrent->next = myContent;
++mylist->size;
}
//遍历
void Foreach_LinklistTwo(LinkListTwo list,void(*FOREACH)(void *))
{
if(nullptr == list)
return;
if(nullptr == FOREACH)
return;
LinkProperty * mylist = (LinkProperty*)list;
LinkContent * pCurrent = mylist->Content.next;
while(pCurrent != nullptr)
{
FOREACH(pCurrent);
pCurrent = pCurrent->next;
}
}
void myPrint_LinklistTwo(void * item)
{
T * data = (T*)item;
qDebug()<<"name:"<<data->name<<" size:"<<data->age;
}
void RemoveBypos_LinklistTwo(LinkListTwo list,int pose)
{
if(nullptr == list)
return;
LinkProperty * mylist = (LinkProperty*)list;
if(pose<0||pose>mylist->size)
return;
LinkContent * pCurrent = &(mylist->Content);
for(int i = 0;i<pose-1;++i)
{
pCurrent = pCurrent->next;
}
LinkContent * pDel = pCurrent->next;
pCurrent->next = pDel->next;
--mylist->size;
}
void Destroy_LinklistTwo(LinkListTwo list)
{
if(nullptr == list)
return;
free(list);
list = nullptr;
}