数据结构:单向链表Part1

目录

2.3线性表的链式存储(单向链表)    

2.3.1线性表的链式存储(单向链表)的设计与实现

初始化

插入 

遍历

删除

清空

大小

销毁

示例:

附录:


2.3线性表的链式存储(单向链表)    

          

整个数据结构中,很重要的一点是,初始化和销毁对应,插入和删除对应,有malloc有free,一定要对应。 

2.3.1线性表的链式存储(单向链表)的设计与实现

初始化

单向链表的结构不同于动态数组,是分散的,数据和数据之间是靠指针来衔接的。

//链表节点数据类型
struct LinkContent
{
    void * data;
    struct LinkContent * next;
};
//链表数据类型
struct LinkProperty
{
    struct LinkContent Content;
    int size;
};

 分两个结构体,第一个结构体是LinkContent,链表内容结构体 链表具体内容:

  1. 指向所存数据的万能指针
  2. 指向自身类型的指针(目的是为了指向下一个结构体)

第二个结构体是Linkproperty ,链表性质结构体 ,包含

  1. 链表具体内容的结构体对象
  2. 链表目前的大小

为了方便可读性,进行了如下操作:

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;
}
发布了85 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_41605114/article/details/104396149