双向循环链表

实现的操作:

在这里插入图片描述

void Init_Double_Circular_List(Double_Circular_List *list);         //初始化双向循环链表
List_Node *__malloc_List_Node(Data_Node *dataNode);                 //为插入元素申请节点空间--方便操作,将内存申请定义为函数
void Insert_Tail(Double_Circular_List *list,Data_Node *dataNode);   //尾部插入结点
void Insert_Head(Double_Circular_List *list,Data_Node *dataNode);   //头部插入结点
void Delete_Tail(Double_Circular_List *list);                       //尾部删除结点
void Delete_Head(Double_Circular_List *list);                       //头部删除结点
void Show_List(Double_Circular_List *list);                         //显示结点元素
void Reverse_List(Double_Circular_List *list);                      //链表逆置
void Show_List_Reverse(Double_Circular_List *list);                 //逆序显示结点元素
boolean isEmpty(Double_Circular_List *list);                        //判断链表结点是否为空
void Get_Size(Double_Circular_List *list);                          //获取链表数量
void Id_Modify(Double_Circular_List *list,int ID);                  //按ID修改结点元素
void Id_Find(Double_Circular_List *list,int ID);                    //按ID查找结点
void Id_Insert(Double_Circular_List *list,Data_Node *dataNode,int ID);//按ID插入结点
void Id_Delete(Double_Circular_List *list,int ID);                    //按ID删除结点
void Sort(Double_Circular_List *list);                                //按ID从小到大排序
void clear(Double_Circular_List *list);                               //清空链表
void Destory(Double_Circular_List *list);                             //销毁链表
void Test(Double_Circular_List *list,Data_Node *dataNode);            //测试案例

结构体定义:

typedef struct 
{   
    int ID;     //存储元素id
    int val;    //此结构体可以存储需要存储的数据元素
}Data_Node;

typedef struct ListNode
{
    Data_Node DataNode;         //保存数据结点
    struct ListNode *pre;       //指向前一个结点      
    struct ListNode *next;      //指向后一个结点
}List_Node;

typedef struct 
{
    List_Node *head;        //头结点
    List_Node *tail;        //尾结点
    int Size;               //存储结点数量
}Double_Circular_List;

相关函数定义:

1、初始化双向循环链表:

void Init_Double_Circular_List(Double_Circular_List *list)
{
    List_Node *Node = (List_Node *)malloc(sizeof(List_Node));
    if(Node == NULL)
    {
        printf("内存申请失败...\n");
        exit(-1);
    }
    //初始化结点指向自身   --- 头结点不存储元素,作为辅助结点方便操作
    Node->next = Node;      
    Node->pre = Node;
    //初始化头尾结点指向Node结点并初始化结点大小为 0 
    list->head = Node;      
    list->tail = Node;
    list->Size = 0;
}

在这里插入图片描述

2、新结点内存空间申请:

List_Node *__malloc_List_Node(Data_Node *dataNode)
{
    List_Node *Node = (List_Node *)malloc(sizeof(List_Node));   //为插入节点申请内存
    if(Node == NULL)
    {
        printf("内存申请失败...\n");
        return NULL;
    }
    Node->DataNode.ID = dataNode->ID;       //新结点的ID 存储插入结点的ID 
    Node->DataNode.val = dataNode->val;     //新结点的val 存储插入结点的val
    Node->next = NULL;                      //结点指向为空
    Node->pre = NULL;                       

    return Node;                            //将申请到的结点返回
}

3、尾部插入结点:

void Insert_Tail(Double_Circular_List *list,Data_Node *dataNode)
{
    List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
    List_Node *nodePre = list->tail;                    //nodePre指向链表尾部结点
    List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点   

    Node->next =  nodePre->next;                        //新结点的next指向尾结点的next指向(即头结点)
    Node->pre = nodePre;                                //新结点的pre指向尾结点

    nodePre->next = Node;                               //尾结点的next指向新结点
    nodeNext->pre = Node;                               //头结点的pre指向新结点

    list->tail = Node;                                  //更新尾结点为新插入结点
    list->Size ++;                                      //结点数量加 1
    
}

在这里插入图片描述

在这里插入图片描述

4、头部插入结点:

void Insert_Head(Double_Circular_List *list,Data_Node *dataNode)
{
    List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
    List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点 

    Node->next = nodeNext->next;                        //新结点的next指向头结点的next指向
    Node->pre = nodeNext;                               //新结点的pre指向头结点或者头结点的next结点的pre指向(两种指向位置相同)亦可用 Node-> pre = nodeNext->next->pre

    nodeNext->next->pre = Node;                          //首结点pre指向新结点
    nodeNext->next = Node;                              //头结点的next指向新结点
    /* 
    在头部插入时有两种情况:
        已经存在结点元素和无结点元素两种 
        一、如果已经存在结点元素以上步骤已经实现插入结点,
        二、如果无结点元素,此结点是第一个结点则需要改变尾指针指向
    */
    if(list->Size == 0)
    {
        list->tail = Node;
    }
    list->Size ++;                                      //结点数量加 1
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5、尾部删除结点:

void Delete_Tail(Double_Circular_List *list) 
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }
    List_Node * tmp = list->head;           //tmp临时指针存储头结点位置
    List_Node *delete = list->tail;         //delete临时指针存储尾结点位置

    delete->pre->next = delete->next;       //尾结点的前一个结点的next指向头结点(或者说是尾结点的next指向)
    tmp->pre = delete->pre;                 //头结点的pre指向尾结点的前一个结点(或者说删除节点的前一个结点)
    
    list->tail = delete->pre;               //更新尾结点为删除结点的前一个结点

    free(delete);                           //释放尾结点空间
    list->Size --;                          //结点元素减 1
}

在这里插入图片描述

6、头部删除结点:

void Delete_Head(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }
    List_Node * tmp = list->head;           //tmp临时指针存储头结点位置
    List_Node *delete = list->head->next;   //delete临时指针存储首结点位置

    delete->next->pre = delete->pre;        //首结点的下一结点的pre指向头结点(或删除节点的pre指向)
    tmp->next = delete->next;               //头结点的next指向首结点的next指向(或删除结点的next指向)

    free(delete);                           //释放首结点空间
    list->Size --;                          //结点元素减 1
}

在这里插入图片描述

7、显示链表内容:

void Show_List(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无结点数据显示。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;           //临时指针保存首结点位置 
    printf("--------------------------------\n");
    printf("|    ID    |        Val        |\n");
    printf("--------------------------------\n");
    while(tmp != list->head)        //当临时结点指向的位置和头结点位置相同时跳出循环
    {
        printf("|%5d    ||%9d          |\n",tmp->DataNode.ID,tmp->DataNode.val);   //输出结点元素             
        tmp = tmp->next;        //指向下一元素
    }
    printf("--------------------------------\n");
    printf("|   size  ||       %d           |\n",list->Size);
    printf("--------------------------------\n\n\n");

}

8、判断链表是否为空:

boolean isEmpty(Double_Circular_List *list)
{
    if(list->head == list->tail)
        return TRUE;
    return FALSE;
}

9、测试

快速插入几个结点方便调试

void Test(Double_Circular_List *list,Data_Node *dataNode)
{
    for(int i = 3;i < 8;i++)
    {
        dataNode->ID = i;
        dataNode->val = i * 10;
        Insert_Tail(list,dataNode);
    }
}

10、获取链表结点数量:

void Get_Size(Double_Circular_List *list)
{
    printf("链表结点数量为:\t%d\n",list->Size);
}

11、链表逆序显示:

void Show_List_Reverse(Double_Circular_List *list)
{
	//从尾结点开始通过指向pre结点完成逆序遍历
    if(isEmpty(list))
    {
        printf("结点元素为空,无结点数据显示。。。\n");
        return ;
    }
    List_Node *tmp = list->tail;           //临时指针保存尾结点位置 
    printf("--------------------------------\n");
    printf("|    ID    |        Val        |\n");
    printf("--------------------------------\n");
    while(tmp != list->head)        //当临时结点指向的位置和头结点位置相同时跳出循环
    {
        printf("|%5d    ||%9d          |\n",tmp->DataNode.ID,tmp->DataNode.val);   //输出结点元素             
        tmp = tmp->pre;        //指向前一元素
    }
}

在这里插入图片描述

12、链表逆置:

本程序采用将原链表结点从首结点开始的结点依次遍历,每遍历一个结点将此结点从头部插入,最终完成逆置

void Reverse_List(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法逆置。。。\n");
        return ;
    }
    /*
        思路:
            遍历结点,每遍历一个结点头部插入; 逆置还可以通过双指针的方法实现,此方法在单链表中也已经实现
    */
    List_Node *tmp = list->head->next;       //获取首结点位置
    List_Node *delete = tmp;

    //断开头结点和首结点间的指向,将头结点恢复为初始化的状态
    list->tail = list->head;
    list->head->next = list->head;
    list->head->pre = list->head;
    list->Size = 0;

    //遍历插入结点
    while(tmp != list->head)
    {
        Insert_Head(list,&tmp->DataNode);    //头部插入
        tmp = tmp->next;     //指向下一结点
        free(delete);       //释放空间
        delete = tmp;       //更新删除结点
    }
}

在这里插入图片描述

13、按ID修改结点元素:

void Id_Modify(Double_Circular_List *list,int ID)
{
    int val;
    if(isEmpty(list))
    {
        printf("结点元素为空,无法修改。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;      //保存首结点元素地址
    //当结点id 和查找的 id不同时结点向下一结点查找
        //当链表被遍历完或者找到id对应结点时结束查找
    while(tmp->DataNode.ID != ID && tmp != list->head) 
    {
        tmp = tmp->next;
    }
    if(tmp == list->head)
    {
        printf("未找到对应 id 结点\n");
        return ;
    }

    printf("请输入要修改的 val :\n");
    scanf("%d",&val);
    tmp->DataNode.val = val;        //将修改后的 val 替换该 id 的 val
}

14、按ID查找结点:

void Id_Find(Double_Circular_List *list,int ID)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法查找。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;  
    //当结点id 和查找的 id不同时结点向下一结点查找
        //当链表被遍历完或者找到id对应结点时结束查找
    while(tmp->DataNode.ID != ID && tmp != list->head)
    {
        tmp = tmp->next;
    }
    //如果tmp == list->head 说明遍历了所有结点均未找到
    if(tmp == list->head)
    {
        printf("未找到对应 id 结点\n");
        return ;
    }

    printf("ID = %d 结点 val 为:\t%d\n",tmp->DataNode.ID,tmp->DataNode.val);
}

15、按ID插入结点:

1、注意判断链表此时是否为空,若为空则直接插入结点
2、若插入结点ID均大于当前链表结点的ID,在尾部插入结点

void Id_Insert(Double_Circular_List *list,Data_Node *dataNode,int ID)
{
    if(!isEmpty(list))        //链表不为空
    {   
        List_Node *tmp = list->head->next;              //存储首结点元素
        //链表的结点ID 均小于插入结点ID时在尾部插入
        while(ID > tmp->DataNode.ID && tmp != list->head)
        {
            tmp = tmp->next;
        }
        //遍历完链表未找到大于插入结点ID的结点,在尾部插入结点
        if(tmp == list->head)
        {
            List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
            List_Node *nodePre = list->tail;                    //nodePre指向链表尾部结点
            List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点   

            Node->next =  nodePre->next;                        //新结点的next指向尾结点的next指向(即头结点)
            Node->pre = nodePre;                                //新结点的pre指向尾结点

            nodePre->next = Node;                               //尾结点的next指向新结点
            nodeNext->pre = Node;                               //头结点的pre指向新结点

            list->tail = Node;                                  //更新尾结点为新插入结点
            list->Size ++;                                      //结点数量加 1
        }
        else    //在中间结点插入的情况
        {
            List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
            
            Node->next = tmp;                        //新结点的next指向ID较大的结点
            Node->pre = tmp->pre;                     //新结点的pre指向ID较大结点的前一个结点

            tmp->pre->next = Node;                    //较大结点的前一个结点指向新结点
            tmp->pre = Node;                           //较大结点的pre指向新结点

            list->Size ++;
        }

    }
    if(isEmpty(list))   //链表结点元素为空时直接插入
    {
        List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
        List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点 

        Node->next = nodeNext;                        //新结点的next指向头结点
        Node->pre = nodeNext;                         //新结点的pre指向头结点

        nodeNext->next = Node;                          //头结点next指向新结点
        nodeNext->pre = Node;                           //头结点的pre指向新结点
     
        list->tail = Node;                              //修改尾指针指向
        
        list->Size ++;                                  //结点数量加 1
    }

}

16、按ID删除结点:

void Id_Delete(Double_Circular_List *list,int ID)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }

    List_Node *tmp = list->head->next;              //存储首结点元素
    //遍历查找
    while(ID != tmp->DataNode.ID && tmp != list->head)
    {
        tmp = tmp->next;
    }
    //如果遍历完所有结点未找到对应ID输出该结点不存在
    if(tmp == list->head)
    {
        printf("ID = %d 的结点不存在\n",ID);
        return;
    }

    tmp->next->pre = tmp->pre;
    tmp->pre->next = tmp->next;

    free(tmp);
    list->Size --;
}

16、排序:

void Sort(Double_Circular_List *list)
{
    /* 
        思路:
            获取首结点的位置,将链表头结点到首结点之间断开,头结点置为初始化的指向,
            从首结点开始遍历结点,将遍历到的结点依次按ID插入结点,同时将遍历结点的内存空间释放
     */
    if(isEmpty(list))
    {
        printf("结点元素为空,无法排序。。。\n");
        return ;
    }

    List_Node *tmp = list->head->next;      //获取首结点
    List_Node *delete = tmp;                //删除结点位置

    //断开头结点和首结点间的指向,初始化头结点
    list->tail = list->head;
    list->head->next = list->head;
    list->head->pre = list->head;
    list->Size = 0;

    //遍历插入结点
    while(tmp != list->head)
    {
        Id_Insert(list,&tmp->DataNode,tmp->DataNode.ID);    //按ID插入
        tmp = tmp->next;     //指向下一结点
        free(delete);       //释放空间
        delete = tmp;       //更新删除结点
    }
}

在这里插入图片描述
在这里插入图片描述

18、清空链表:

void clear(Double_Circular_List *list)
{
    /* 
        思路:
            遍历结点,每访问一个结点从头部删除一个结点
     */
    if(isEmpty(list))
    {
        printf("结点元素为空。。。\n");
        return ;
    } 

    List_Node *tmp = list->head->next;  //获取首结点位置
    //遍历删除结点,当临时指针和头结点指向相同时遍历结束
    while(tmp != list->head)
    {
        tmp = tmp->next;    //指向下一结点
        Delete_Head(list);  //头部删除
    }
}

19、销毁链表:

void Destory(Double_Circular_List *list)
{
    //先将链表清空
    clear(list);
    //释放头结点空间
    free(list->head);
}

完整程序:

.c文件:

#include "linked_list.h"

void main()
{
    Data_Node Datanode;
    Double_Circular_List List;
    int ID;

    Init_Double_Circular_List(&List);

    int operation = 1;
    while(operation)
    {
        printf("-----------------------------------------------\n");
		printf("|              双向循环链表                    |\n");
		printf("-----------------------------------------------\n");
		printf("|  [1] 尾部插入结点        [2] 头部插入结点    |\n");
		printf("|  [3] 尾部删除结点        [4] 头部删除结点    |\n");
		printf("|  [5] 按ID插入结点        [6] 显示链表内容    |\n");
		printf("|  [7] 按ID删除结点        [8] 按ID修改结点元素|\n");
		printf("|  [9] 按ID查找结点        [10] 获取结点数量   |\n");
		printf("|  [11] 排序               [12] 链表逆序显示   |\n");
		printf("|  [13] 清空链表           [14] 销毁链表       |\n");
		printf("|  [15] 测试               [16] 链表逆置       |\n");
		printf("|  [0] 退出程序                                |\n");
		printf("-----------------------------------------------\n");

		printf("请输入操作序号 :\n");
		scanf("%d",&operation);

        switch(operation)
        {
            case 0:
                printf("退出程序\n");
                Sleep(1000);
                exit(-1);
                break;
            case 1:
                printf("尾部插入结点:\n");
                printf("请输入结点ID 、存储数据 val :\n");
                scanf("%d %d",&Datanode.ID,&Datanode.val);
                Insert_Tail(&List,&Datanode);
                break;

            case 2:
                printf("头部插入结点:\n");
                printf("请输入结点ID 、存储数据 val :\n");
                scanf("%d %d",&Datanode.ID,&Datanode.val);
                Insert_Head(&List,&Datanode);
                break;

            case 3:
                printf("尾部删除结点:\n");
                Delete_Tail(&List);
                break;

            case 4:
                printf("头部删除结点:\n");
                Delete_Head(&List);
                break;

            case 5:
                printf("按ID插入结点:\n");
                printf("请输入结点ID 、存储数据 val :\n");
                scanf("%d %d",&Datanode.ID,&Datanode.val);
                Id_Insert(&List,&Datanode,Datanode.ID);
                break;

            case 6:
                printf("显示链表元素:\n");
                Show_List(&List);
                break;

            case 7:
                printf("按ID删除节点:\n");
                printf("请输入要删除结点ID:\n");
                scanf("%d",&ID);
                Id_Delete(&List,ID);
                break;

            case 8:
                printf("按ID修改结点元素:\n");
                printf("请输入修改结点ID:\n");
                scanf("%d",&ID);
                Id_Modify(&List,ID);
                break;
            case 9:
                printf("按ID查找结点元素:\n");
                printf("请输入查找结点ID:\n");
                scanf("%d",&ID);
                Id_Find(&List,ID);
                break;
            case 10:
                printf("显示结点数量:\n");
                Get_Size(&List);
                break;
            case 11:
                printf("链表排序:\n");
                Sort(&List);
                break;
            case 12:
                printf("逆序显示链表元素:\n");
                Show_List_Reverse(&List);
                break;
            case 13:
                printf("清空链表\n");
                clear(&List);
                break;
            case 14:
                printf("销毁链表:\n");
                Destory(&List);
                break;
            case 15:
                Test(&List,&Datanode);
                break;
            case 16:
                printf("链表逆置\n");
                Reverse_List(&List);
                break;
            default:
                printf("请输入 0-15之间的数字:\n ");
                break;

        }

    }

    system("pause");
    return ;
}

void Init_Double_Circular_List(Double_Circular_List *list)
{
    List_Node *Node = (List_Node *)malloc(sizeof(List_Node));
    if(Node == NULL)
    {
        printf("内存申请失败...\n");
        exit(-1);
    }
    //初始化结点指向自身   --- 头结点不存储元素,作为辅助结点方便操作
    Node->next = Node;      
    Node->pre = Node;
    //初始化头尾结点指向Node结点并初始化结点大小为 0 
    list->head = Node;      
    list->tail = Node;
    list->Size = 0;
}

List_Node *__malloc_List_Node(Data_Node *dataNode)
{
    List_Node *Node = (List_Node *)malloc(sizeof(List_Node));   //为插入节点申请内存
    if(Node == NULL)
    {
        printf("内存申请失败...\n");
        return NULL;
    }
    Node->DataNode.ID = dataNode->ID;       //新结点的ID 存储插入结点的ID 
    Node->DataNode.val = dataNode->val;     //新结点的val 存储插入结点的val
    Node->next = NULL;                      //结点指向为空
    Node->pre = NULL;                       

    return Node;                            //将申请到的结点返回
}

void Insert_Tail(Double_Circular_List *list,Data_Node *dataNode)
{
    List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
    List_Node *nodePre = list->tail;                    //nodePre指向链表尾部结点
    List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点   

    Node->next =  nodePre->next;                        //新结点的next指向尾结点的next指向(即头结点)
    Node->pre = nodePre;                                //新结点的pre指向尾结点

    nodePre->next = Node;                               //尾结点的next指向新结点
    nodeNext->pre = Node;                               //头结点的pre指向新结点

    list->tail = Node;                                  //更新尾结点为新插入结点
    list->Size ++;                                      //结点数量加 1
    
}

void Insert_Head(Double_Circular_List *list,Data_Node *dataNode)
{
    List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
    List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点 

    Node->next = nodeNext->next;                        //新结点的next指向头结点的next指向
    Node->pre = nodeNext;                               //新结点的pre指向头结点或者头结点的next结点的pre指向(两种指向位置相同)亦可用 Node-> pre = nodeNext->next->pre

    nodeNext->next->pre = Node;                          //首结点pre指向新结点
    nodeNext->next = Node;                              //头结点的next指向新结点
    /* 
    在头部插入时有两种情况:
        已经存在结点元素和无结点元素两种 
        一、如果已经存在结点元素以上步骤已经实现插入结点,
        二、如果无结点元素,此结点是第一个结点则需要改变尾指针指向
    */
    if(list->Size == 0)
    {
        list->tail = Node;
    }
    list->Size ++;                                      //结点数量加 1
}

void Show_List(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无结点数据显示。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;           //临时指针保存首结点位置 
    printf("--------------------------------\n");
    printf("|    ID    |        Val        |\n");
    printf("--------------------------------\n");
    while(tmp != list->head)        //当临时结点指向的位置和头结点位置相同时跳出循环
    {
        printf("|%5d    ||%9d          |\n",tmp->DataNode.ID,tmp->DataNode.val);   //输出结点元素             
        tmp = tmp->next;        //指向下一元素
    }
    printf("--------------------------------\n");
    printf("|   size  ||       %d           |\n",list->Size);
    printf("--------------------------------\n\n\n");

}

void Delete_Tail(Double_Circular_List *list) 
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }
    List_Node * tmp = list->head;           //tmp临时指针存储头结点位置
    List_Node *delete = list->tail;         //delete临时指针存储尾结点位置

    delete->pre->next = delete->next;       //尾结点的前一个结点的next指向头结点(或者说是尾结点的next指向)
    tmp->pre = delete->pre;                 //头结点的pre指向尾结点的前一个结点(或者说删除节点的前一个结点)
    
    list->tail = delete->pre;               //更新尾结点为删除结点的前一个结点

    free(delete);                           //释放尾结点空间
    list->Size --;                          //结点元素减 1

}

void Delete_Head(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }
    List_Node * tmp = list->head;           //tmp临时指针存储头结点位置
    List_Node *delete = list->head->next;   //delete临时指针存储首结点位置

    delete->next->pre = delete->pre;        //首结点的下一结点的pre指向头结点(或删除节点的pre指向)
    tmp->next = delete->next;               //头结点的next指向首结点的next指向(或删除结点的next指向)

    free(delete);                           //释放首结点空间
    list->Size --;                          //结点元素减 1
}

boolean isEmpty(Double_Circular_List *list)
{
    if(list->head == list->tail)
        return TRUE;
    return FALSE;
}

void Test(Double_Circular_List *list,Data_Node *dataNode)
{
    for(int i = 3;i < 8;i++)
    {
        dataNode->ID = i;
        dataNode->val = i * 10;
        Insert_Tail(list,dataNode);
    }
}

void Get_Size(Double_Circular_List *list)
{
    printf("链表结点数量为:\t%d\n",list->Size);
}

void Show_List_Reverse(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无结点数据显示。。。\n");
        return ;
    }
    List_Node *tmp = list->tail;           //临时指针保存尾结点位置 
    printf("--------------------------------\n");
    printf("|    ID    |        Val        |\n");
    printf("--------------------------------\n");
    while(tmp != list->head)        //当临时结点指向的位置和头结点位置相同时跳出循环
    {
        printf("|%5d    ||%9d          |\n",tmp->DataNode.ID,tmp->DataNode.val);   //输出结点元素             
        tmp = tmp->pre;        //指向前一元素
    }
}

void Id_Modify(Double_Circular_List *list,int ID)
{
    int val;
    if(isEmpty(list))
    {
        printf("结点元素为空,无法修改。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;      //保存首结点元素地址
    //当结点id 和查找的 id不同时结点向下一结点查找
        //当链表被遍历完或者找到id对应结点时结束查找
    while(tmp->DataNode.ID != ID && tmp != list->head) 
    {
        tmp = tmp->next;
    }
    if(tmp == list->head)
    {
        printf("未找到对应 id 结点\n");
        return ;
    }

    printf("请输入要修改的 val :\n");
    scanf("%d",&val);
    tmp->DataNode.val = val;        //将修改后的 val 替换该 id 的 val
}

void Id_Find(Double_Circular_List *list,int ID)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法查找。。。\n");
        return ;
    }
    List_Node *tmp = list->head->next;  
    //当结点id 和查找的 id不同时结点向下一结点查找
        //当链表被遍历完或者找到id对应结点时结束查找
    while(tmp->DataNode.ID != ID && tmp != list->head)
    {
        tmp = tmp->next;
    }
    if(tmp == list->head)
    {
        printf("未找到对应 id 结点\n");
        return ;
    }

    printf("ID = %d 结点 val 为:\t%d\n",tmp->DataNode.ID,tmp->DataNode.val);
}

void Id_Insert(Double_Circular_List *list,Data_Node *dataNode,int ID)
{
    if(!isEmpty(list))        //链表不为空
    {   
        List_Node *tmp = list->head->next;              //存储首结点元素
        //链表的结点ID 均小于插入结点ID时在尾部插入
        while(ID > tmp->DataNode.ID && tmp != list->head)
        {
            tmp = tmp->next;
        }
        if(tmp == list->head)
        {
            List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
            List_Node *nodePre = list->tail;                    //nodePre指向链表尾部结点
            List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点   

            Node->next =  nodePre->next;                        //新结点的next指向尾结点的next指向(即头结点)
            Node->pre = nodePre;                                //新结点的pre指向尾结点

            nodePre->next = Node;                               //尾结点的next指向新结点
            nodeNext->pre = Node;                               //头结点的pre指向新结点

            list->tail = Node;                                  //更新尾结点为新插入结点
            list->Size ++;                                      //结点数量加 1
        }
        else    //在中间结点插入的情况
        {
            List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
            
            Node->next = tmp;                        //新结点的next指向ID较大的结点
            Node->pre = tmp->pre;                     //新结点的pre指向ID较大结点的前一个结点

            tmp->pre->next = Node;                    //较大结点的前一个结点指向新结点
            tmp->pre = Node;                           //较大结点的pre指向新结点

            list->Size ++;
        }

    }
    if(isEmpty(list))   //链表结点元素为空时直接插入
    {
        List_Node *Node = __malloc_List_Node(dataNode);     //获取申请到的结点
        List_Node *nodeNext = list->head;                   //nodeNext指向链表头结点 

        Node->next = nodeNext;                        //新结点的next指向头结点
        Node->pre = nodeNext;                         //新结点的pre指向头结点

        nodeNext->next = Node;                          //头结点next指向新结点
        nodeNext->pre = Node;                           //头结点的pre指向新结点
     
        list->tail = Node;                              //修改尾指针指向
        
        list->Size ++;                                  //结点数量加 1
    }

}

void Id_Delete(Double_Circular_List *list,int ID)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法删除。。。\n");
        return ;
    }

    List_Node *tmp = list->head->next;              //存储首结点元素
    //遍历查找
    while(ID != tmp->DataNode.ID && tmp != list->head)
    {
        tmp = tmp->next;
    }
    //如果遍历完所有结点未找到对应ID输出该结点不存在
    if(tmp == list->head)
    {
        printf("ID = %d 的结点不存在\n",ID);
        return;
    }

    tmp->next->pre = tmp->pre;
    tmp->pre->next = tmp->next;

    free(tmp);
    list->Size --;
}

void Sort(Double_Circular_List *list)
{
    /* 
        思路:
            获取首结点的位置,将链表头结点到首结点之间断开,头结点置为初始化的指向,
            从首结点开始遍历结点,将遍历到的结点依次按ID插入结点,同时将遍历结点的内存空间释放
     */
    if(isEmpty(list))
    {
        printf("结点元素为空,无法排序。。。\n");
        return ;
    }

    List_Node *tmp = list->head->next;      //获取首结点
    List_Node *delete = tmp;                //删除结点位置

    //断开头结点和首结点间的指向,初始化头结点
    list->tail = list->head;
    list->head->next = list->head;
    list->head->pre = list->head;
    list->Size = 0;

    //遍历插入结点
    while(tmp != list->head)
    {
        Id_Insert(list,&tmp->DataNode,tmp->DataNode.ID);    //按ID插入
        tmp = tmp->next;     //指向下一结点
        free(delete);       //释放空间
        delete = tmp;       //更新删除结点
    }
}

void clear(Double_Circular_List *list)
{
    /* 
        思路:
            遍历结点,每访问一个结点从头部删除一个结点
     */
    if(isEmpty(list))
    {
        printf("结点元素为空。。。\n");
        return ;
    } 

    List_Node *tmp = list->head->next;  //获取首结点位置
    //遍历删除结点,当临时指针和头结点指向相同时遍历结束
    while(tmp != list->head)
    {
        tmp = tmp->next;    //指向下一结点
        Delete_Head(list);  //头部删除
    }
}

void Destory(Double_Circular_List *list)
{
    //先将链表清空
    clear(list);
    //释放头结点空间
    free(list->head);
}

void Reverse_List(Double_Circular_List *list)
{
    if(isEmpty(list))
    {
        printf("结点元素为空,无法逆置。。。\n");
        return ;
    }
    /*
        思路:
            遍历结点,每遍历一个结点头部插入; 逆置还可以通过双指针的方法实现,此方法在单链表中也已经实现
    */
    List_Node *tmp = list->head->next;       //获取首结点位置
    List_Node *delete = tmp;

    //断开头结点和首结点间的指向,初始化头结点
    list->tail = list->head;
    list->head->next = list->head;
    list->head->pre = list->head;
    list->Size = 0;

    //遍历插入结点
    while(tmp != list->head)
    {
        Insert_Head(list,&tmp->DataNode);    //头部插入
        tmp = tmp->next;     //指向下一结点
        free(delete);       //释放空间
        delete = tmp;       //更新删除结点
    }
}

.h文件:

#include <windows.h>
#include <stdio.h>

typedef struct 
{   
    int ID;     //存储元素id
    int val;    //此结构体可以存储需要存储的数据元素
}Data_Node;

typedef struct ListNode
{
    Data_Node DataNode;         //保存数据结点
    struct ListNode *pre;       //指向前一个结点      
    struct ListNode *next;      //指向后一个结点
}List_Node;

typedef struct 
{
    List_Node *head;        //头结点
    List_Node *tail;        //尾结点
    int Size;               //存储结点数量
}Double_Circular_List;


void Init_Double_Circular_List(Double_Circular_List *list);         //初始化双向循环链表
List_Node *__malloc_List_Node(Data_Node *dataNode);                 //为插入元素申请节点--方便操作,将内存申请定义为函数
void Insert_Tail(Double_Circular_List *list,Data_Node *dataNode);   //尾部插入结点
void Insert_Head(Double_Circular_List *list,Data_Node *dataNode);   //头部插入结点
void Delete_Tail(Double_Circular_List *list);                       //尾部删除结点
void Delete_Head(Double_Circular_List *list);                       //头部删除结点
void Show_List(Double_Circular_List *list);                         //显示结点元素
void Reverse_List(Double_Circular_List *list);                      //链表逆置
void Show_List_Reverse(Double_Circular_List *list);                 //逆序显示结点元素
boolean isEmpty(Double_Circular_List *list);                        //判断链表结点是否为空
void Get_Size(Double_Circular_List *list);                          //获取链表数量
void Id_Modify(Double_Circular_List *list,int ID);                  //按ID修改结点元素
void Id_Find(Double_Circular_List *list,int ID);                    //按ID查找结点
void Id_Insert(Double_Circular_List *list,Data_Node *dataNode,int ID);//按ID插入结点
void Id_Delete(Double_Circular_List *list,int ID);                    //按ID删除结点
void Sort(Double_Circular_List *list);                                //按ID从小到大排序
void clear(Double_Circular_List *list);                               //清空链表
void Destory(Double_Circular_List *list);                             //销毁链表
void Test(Double_Circular_List *list,Data_Node *dataNode);            //测试案例

参考:B站—IT扫地僧
声明:此文章为学习笔记,如有侵权请联系删除。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_Byte_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值