循环链表

来源:互联网 发布:求最小公倍数的vb算法 编辑:程序博客网 时间:2024/06/07 03:09

循环链表

  • 循环链表
    • 基本概念
    • 设计与实现
    • 代码实现
    • 优缺点
    • 约瑟夫问题-循环链表典型应用

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 0
原创粉丝点击