循环链表

循环链表

1.基本概念

循环链表的定义:
  将单链表中最后一个数据元素(业务节点)的next指针指向第一个元素
  这里写图片描述
循环链表拥有单链表的所有操作

创建链表
销毁链表
获取链表长度
清空链表
获取第pos个元素操作
插入元素到位置pos
删除位置pos处的元素

新增功能游标的定义
  在循环链表中可以定义一个“当前”指针,这个指针通常称为游标,可以通过这个游标来遍历链表中的所有元素。

这里写图片描述

循环链表新操作

  • 获取当前游标指向的数据元素
  • 将游标重置指向链表中的第一个数据元素
  • 将游标移动指向到链表中的下一个数据元素
  • 直接指定删除链表中的某个数据元素
CircleListNode* CircleList_DeleteNode(CircleList* list, CircleListNode* node);
CircleListNode* CircleList_Reset(CircleList* list);
CircleListNode* CircleList_Current(CircleList* list);
CircleListNode* CircleList_Next(CircleList* list);

2.设计与实现

  • 插入元素的分析
    1) 普通位置插入元素
    2) 添加第一个元素(第一次插入元素)
    3) 最后一个位置插入元素
    4) 第一个位置插入元素
    在第一个位置插入
    这里写图片描述
    这里写图片描述
    这里写图片描述

  • 删除节点
    这里写图片描述

3.代码实现

  • 头文件
#ifndef __CIRCLE_LIST_H__
#define __CIRCLE_LIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

typedef  void CircleList;//数据类型的封装


//业务数据节点
typedef struct _tag_MyCicleListNode
{
    struct _tag_MyCicleListNode * next;//只有指针域
}CircleListNode;

CircleList *CircleList_Create();

void CircleList_Destroy(CircleList * list);

void CircleList_Clear(CircleList * list);

int CircleList_Length(CircleList *list);

int CircleList_Insert(CircleList * list, CircleListNode * node,int pos);

CircleListNode * CircleList_Get(CircleList * list,int pos);

CircleListNode * CircleList_Delete(CircleList * list,int pos);

CircleListNode * CircleList_DeleteNode(CircleList * list,CircleListNode *node);

CircleListNode * CircleList_Reset(CircleList * list);

CircleListNode * CircleList_Current(CircleList * list);

CircleListNode * CircleList_Next(CircleList * list);

#endif // !__CIRCLE_LIST_H__
  • 实现文件
#include "CircleList.h"

//表头结点
typedef struct _tagMyCircleList
{
    CircleListNode head;//表头结点里面的头结点,
                        //里面存放的是指向链表第一个节点的指针
    CircleListNode * slider;//游标
    int length;//业务数据节点个数
}TCircleList;


/*创建链表*/
CircleList *CircleList_Create()
{
    TCircleList * tmp = NULL;

    /*为表头结点分配内存*/
    tmp = (TCircleList *)malloc(sizeof(TCircleList));
    if (NULL == tmp)//合法性检测
    {
        printf("malloc error!\n");
        return NULL;
    }

    /*初始化表头节点*/
    memset(tmp,0,sizeof(TCircleList));

    /*返回分配好内存的循环链表表头节点首地址*/
    return tmp;
}

/*销毁链表*/
void CircleList_Destroy(CircleList * list)
{
    if (list != NULL)
    {
        free(list);
        list = NULL;
    }
    else{
        printf("argv error!\n");
    }
    return ;
}

/*清空链表*/
void CircleList_Clear(CircleList * list)
{
    TCircleList * tmp = NULL;

    if (list == NULL)
    {
        printf("argv error!\n");
        return;
    }
    tmp = (TCircleList *)list;

    /*全部恢复初始状态*/
    tmp->head.next = NULL;
    tmp->slider = NULL;
    tmp->length = 0;

    return ;
}

/*获取业务节点个数*/
int CircleList_Length(CircleList *list)
{
    TCircleList * tmp = NULL;

    if (list == NULL)
    {
        printf("argv error!\n");
        return -1;
    }
    tmp = (TCircleList *)list;
    return tmp->length;

}

/*插入节点*/
int CircleList_Insert(CircleList * list, CircleListNode * node, int pos)
{
    TCircleList * tmp = NULL;//表头结点辅助指针
    CircleListNode * current = NULL;//当前节点指针
    CircleListNode * last_node = NULL;//尾节点辅助指针
    int  i = 0;//循环变量

    /*1. 合法性检测*/
    if (list == NULL || node  == NULL || pos < 0)
    {
        printf("argv error!\n");
        return -1;
    }
    tmp = (TCircleList *)list;

    /*if (pos >= tmp->length)//循环链表不能进行容错纠正,
                            //否则会卡死在第二次循环遍历的第一个节点
    {
        pos = tmp->length;
    }*/

    /*2.当前指针指向表头结点*/
    current = &(tmp->head);

    /*3.向后跳pos个节点,使得当前指针指向pos-1个节点*/
    for (i = 0; i < pos && current->next != NULL;i++)
    {
        current = current->next;
    }

    /*4.进行常规的插入操作*/
    node->next = current->next;
    current->next = node;

    /*5.判断此次插入操作是否是第一次插入操作*/
    if (tmp->length == 0)
    {
        tmp->slider = node;//如果是第一次操作,更新游标指向第一个插入的节点
    }

    /*6.更新此时链表的长度*/
    tmp->length++;

    /*7.判断此次插入是否是从表头插入(头插法)*/
    if (current == &(tmp->head))
    {
        /*7.1 获取此时链表中最后一个业务节点*/
        last_node = CircleList_Get((CircleList *)tmp, tmp->length - 1);

        /*7.2 将尾节点指向新插入的头结点*/
        last_node->next = node;
    }

    /*8. 插入成功返回0*/
    return 0;
}

CircleListNode * CircleList_Get(CircleList * list, int pos)
{
    /*相关辅助变量的声明*/
    TCircleList * tmp = NULL;
    CircleListNode * current = NULL;
    int  i = 0;

    /*合法性检测*/
    if (list == NULL  || pos < 0)
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    /*if (pos >= tmp->length)
    {
        pos = tmp->length;
    }*/

    /*当前节点指针的初始化--指向表头结点*/
    current = &(tmp->head);

    /*向后跳pos个节点--当前指针指向第pos-1个节点*/
    for (i = 0; i < pos && current->next != NULL; i++)
    {
        current = current->next;
    }

    /*返回目标节点*/
    return current->next;
}

CircleListNode * CircleList_Delete(CircleList * list, int pos)
{
    /*1. 相关辅助变量的声明*/
    TCircleList * tmp = NULL;//表头结点指针
    CircleListNode * current = NULL;//当前节点指针
    CircleListNode * res_node = NULL;//要删除的节点指针
    CircleListNode * last_node = NULL;//最后一个节点指针
    int  i = 0;//循环变量

    /*2. 合法性检测*/
    if (list == NULL  || pos < 0 )
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    /*3. 防止对空链表进行删除操作*/
    if (tmp->length <= 0)
    {
        printf("length error!\n");
        return NULL;
    }
    /*if (pos >= tmp->length)
    {
    pos = tmp->length;
    }*/

    /*4. 初始化当前节点指针*/
    current = &(tmp->head);

    /*5. 向后跳pos个节点*/
    for (i = 0; i < pos && current->next != NULL; i++)
    {
        current = current->next;
    }

    /*6. 判断是否是对头结点进行删除操作*/
    if (current == &(tmp->head))
    {
        //如果删除头结点,要先获取最后一个节点的指针在进行删除操作
        last_node = CircleList_Get((CircleList *)tmp, tmp->length - 1);
    }

    /*7. 进行常规的删除操作*/
    res_node = current->next;//获取要删除的节点
    current->next = res_node->next;

    /*8. 修改此时链表长度*/
    tmp->length--;

    /*9. 如果删除头结点则需要更新最后一个节点的指向,
    否则最后一个节点指向的是一块非法内存空间*/
    if (last_node != NULL)
    {
        last_node->next = current->next;
        tmp->head.next = current->next;
    }

    /*10 .如果删除的节点是游标所指的节点*/
    if (tmp->slider == res_node)
    {   
        //将游标指向下一个节点
        tmp->slider = res_node->next;
    }

    /*11. 如果删除该节点以后是空链表--进行清空操作*/
    if (tmp->length == 0)
    {
        tmp->head.next = NULL;
        tmp->slider = NULL;
    }

    /*12. 返回被删除的节点*/
    return res_node;
}

/*从给定的链表中删除指定的节点*/
CircleListNode * CircleList_DeleteNode(CircleList * list, CircleListNode *node)
{
    /*相关辅助变量声明*/
    TCircleList * tmp = NULL;
    CircleListNode * current = NULL;
    CircleListNode * res_node = NULL;//指向要删除的节点
    int  i = 0;

    /*参数合法性检测*/
    if (list == NULL || node == NULL)
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    /*当前节点辅助指针变量初始化--指向表头结点*/
    current = &(tmp->head);

    /*遍历链表,寻找与给定节点相同的节点*/
    for (i = 0; i < tmp->length && current->next != NULL;i++)
    {
        /*找到目标节点*/
        if (current->next == node)
        {
            res_node = current->next;//记录目标节点的位置
            break;
        }
        current = current->next;//更新当前节点辅助指针变量
    }

    /*确实找到目标节点*/
    if (res_node != NULL)
    {
        /*调用节点删除函数*/
        CircleList_Delete((CircleList *)tmp, i);//此时的i就是目标节点在链表相应的pos
    }

    /*返回目标节点*/
    return res_node;
}

/*使链表的游标复位--指向第一个业务节点*/
CircleListNode * CircleList_Reset(CircleList * list)
{

    TCircleList * tmp = NULL;

    /*合法性*/
    if (list == NULL)
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    /*更新游标指向第一个业务节点*/
    tmp->slider = tmp->head.next;

    return tmp->slider;
}


/*返回当前游标指向的节点*/
CircleListNode * CircleList_Current(CircleList * list)
{
    TCircleList * tmp = NULL;
    /*合法性*/
    if (list == NULL)
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    return tmp->slider;//返回当前游标指向的节点
}

/*返回当前游标指向的节点并把游标指向下一个节点*/
CircleListNode * CircleList_Next(CircleList * list)
{
    TCircleList * tmp = NULL;
    CircleListNode * res_node = NULL;//保存当前游标指向的节点

    /*合法性*/
    if (list == NULL)
    {
        printf("argv error!\n");
        return NULL;
    }
    tmp = (TCircleList *)list;

    /*防止是空链表--游标是空指针*/
    if (tmp->slider == NULL)
    {
        printf("tmp->slider error!\n");
        return NULL;
    }

    res_node = tmp->slider;//记录当前游标指向的节点
    tmp->slider = res_node->next;//更新游标指向下一个节点

    /*返回当前节点*/
    return res_node;
}
  • 测试文件
#include "CircleList.h"

struct Value
{
    CircleListNode header;
    int v;
};

void  main()
{
    int i = 0;
    CircleList* list = CircleList_Create();

    struct Value v1;
    struct Value v2;
    struct Value v3;
    struct Value v4;
    struct Value v5;
    struct Value v6;
    struct Value v7;
    struct Value v8;

    v1.v = 1;
    v2.v = 2;
    v3.v = 3;
    v4.v = 4;
    v5.v = 5;
    v6.v = 6;
    v7.v = 7;
    v8.v = 8;

    CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list));

    CircleList_Insert(list, (CircleListNode*)&v5, 5);
    CircleList_Delete(list, 0);

    for (i = 0; i<2 * CircleList_Length(list); i++)
    {
        struct Value* pv = (struct Value*)CircleList_Get(list, i);
        printf("%d\n", pv->v);
    }

    printf("\n");

    while (CircleList_Length(list) > 0)
    {
        struct Value* pv = (struct Value*)CircleList_Delete(list, 0);

        printf("%d\n", pv->v);
    }

    printf("aloha\n");

    CircleList_Destroy(list);

    system("pause");
    return;
}

4.优缺点

优点:功能强了。

  • 循环链表只是在单链表的基础上做了一个加强,循环链表可以完全取代单链表的使用;
  • 循环链表的Next和Current操作可以高效的遍历链表中的所有元素

缺点:

代码复杂度提高了

5.约瑟夫问题-循环链表典型应用

  n 个人围成一个圆圈,首先第 1 个人从 1 开始一个人一个人顺时针报数,报到第 m 个人,令其出列。然后再从下一 个人开始从 1 顺时针报数,报到第 m 个人,再令其出列,…,如此下去,求出列顺序。

这里写图片描述

#include "CircleList.h"

struct Value
{
    CircleListNode header;
    int v;
};

void  main()
{
    int i = 0;
    CircleList* list = CircleList_Create();

    struct Value v1, v2, v3, v4, v5, v6, v7, v8;

    v1.v = 1;   v2.v = 2;   v3.v = 3;   v4.v = 4;
    v5.v = 5;   v6.v = 6;   v7.v = 7;   v8.v = 8;

    CircleList_Insert(list, (CircleListNode*)&v1, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v2, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v3, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v4, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v5, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v6, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v7, CircleList_Length(list));
    CircleList_Insert(list, (CircleListNode*)&v8, CircleList_Length(list));


    for (i = 0; i < CircleList_Length(list); i++)
    {
        //获取游标所指元素,然后游标下移
        struct Value* pv = (struct Value*)CircleList_Next(list);
        printf("%d\n", pv->v);
    }

    printf("\n");


    //重置游标
    CircleList_Reset(list);

    while (CircleList_Length(list) > 0)
    {
        struct Value* pv = NULL;
        for (i = 1; i < 3; i++)
        {
            CircleList_Next(list);
        }
        pv = (struct Value*)CircleList_Current(list);
        printf("%d\n", pv->v);
        CircleList_DeleteNode(list, (CircleListNode*)pv);
    }

    CircleList_Destroy(list);

    system("pause");
    return;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值