C语言数据结构——循环链表

一、基本概念

循环链表的定义:将单链表中的最后一个元素next指针指向第一个元素
在这里插入图片描述
和我们上面所写的链表除了已经具备的基本功能以外,还要新增一个功能:游标的定义
在这里插入图片描述
在循环链表中可以定义一个“当前”指针,这个指针通常被称为游标,可以通过这个游标来遍历链表中的所有元素。
新增功能:
(1)将游标重置指向链表中的第一个数据元素
(2)获取当前游标指向的数据元素
(3)将游标移动指向到链表中的下一个数据元素
(4)直接指定删除链表中的某个元素

其中(1)(2)(3)都是和我们的游标slider相关的,而(4)是我们新添加的一个方法,根据输入的值去删除结点。

问题:
1.如何证明是循环链表?

使用CircleList_Get方法,放进一个循环内,循环的条件判断句是,链表总长度*2,这样就自然会遍历的时候,从头到尾1234再来一次1234,(假设链表的结点就是1234),那么我们就会输出12341234,就完成了一次循环链表的遍历

二、设计与实现

还是先书写header文件,因为和之前的方法相比,只是新增加了四个方法,所以前面的方法和参数可以照抄,然后再新添加四个方法



#ifndef CPRIMERPLUS_HEADER_H
#define CPRIMERPLUS_HEADER_H
typedef void CircleList;

typedef struct _tag_CircleListNode
{
    struct _tag_CircleListNode * 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);

//新添加的四个方法

//(1)将游标重置指向链表中的第一个数据元素
CircleList  *circleList_Reset(CircleList *list);

//(2)获取当前游标指向的数据元素
CircleList *circleList_current(CircleList *list);

//(3)将游标移动指向到链表中的下一个数据元素
CircleList *circleList_Next(CircleList *list);

//(4)直接指定删除链表中的某个元素
CircleList *circleList_DeleteMode(CircleList *list);

#endif //CPRIMERPLUS_HEADER_H

紧接着,还是写我们的main方法,将header.c头文件拟定好的方法串起来,实现一整套的功能,另外,嵌入元素还是使用的头插法,会在后续双向链表中使用尾插法。

#include <stdio.h>
#include <stdlib.h>
#include "header.h"


struct Value
{
    CircleListNode circlenode;
    int v;
};

int main()
{
    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;

    int i = 0;

    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, 0);
    CircleList_Insert(list, (CircleListNode*)&v2, 0);
    CircleList_Insert(list, (CircleListNode*)&v3, 0);
    CircleList_Insert(list, (CircleListNode*)&v4, 0);
    CircleList_Insert(list, (CircleListNode*)&v5, 0);
    CircleList_Insert(list, (CircleListNode*)&v6, 0);
    CircleList_Insert(list, (CircleListNode*)&v7, 0);
    CircleList_Insert(list, (CircleListNode*)&v8, 0);

    for(i=0; i<2*CircleList_Length(list); i++) //怎么样证明是循环链表
    {
        struct Value* pv = (struct Value*)CircleList_Get(list, i);

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

    while( CircleList_Length(list) > 0 )
    {
        CircleList_Delete(list, 0);
    }

    printf("\n");

    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_DeleteMode(list, (CircleListNode*)pv);
    }

    CircleList_Destroy(list);

    return 0;
}

其实和之前的区别就在于在循环链表的main方法中添加了关于游标的实现。
下面开始写具体方法的实现,下面只介绍关于游标的三个方法,和新添加的根据Value删除结点的Delete_Node方法,其余方法可以在之前的博客中找到。

//把当前位置返回,并且游标下移
CircleListNode* circleList_Next(CircleList* list) 
{
    TCircleList* sList = (TCircleList*)list;
    CircleListNode* ret = NULL;

    if( (sList != NULL) && (sList->slider != NULL) )
    {
        ret = sList->slider;
        sList->slider = ret->next;
    }

    return ret;
}

CircleListNode* circleList_DeleteMode(CircleList* list, CircleListNode* node) // O(n)
{
    TCircleList* sList = (TCircleList*)list;
    CircleListNode* ret = NULL;
    int i = 0;

    if( sList != NULL )
    {
        CircleListNode* current = (CircleListNode*)sList;

        //查找node在循环链表中的位置i
        for(i=0; i<sList->length; i++)
        {
            if( current->next == node )
            {
                ret = current->next;
                break;
            }

            current = current->next;
        }

        //如果ret找到,根据i去删除
        if( ret != NULL )
        {
            CircleList_Delete(sList, i);
        }
    }

    return ret;
}

CircleListNode* circleList_Reset(CircleList* list) // O(1)
{
    TCircleList* sList = (TCircleList*)list;
    CircleListNode* ret = NULL;

    if( sList != NULL )
    {
        sList->slider = sList->header.next;
        ret = sList->slider;
    }

    return ret;
}

CircleListNode* circleList_current(CircleList* list) // O(1)
{
    TCircleList* sList = (TCircleList*)list;
    CircleListNode* ret = NULL;

    if( sList != NULL )
    {
        ret = sList->slider;
    }

    return ret;
}

三、优点和缺点

优点:只是比单链表的功能强了,多了几个功能
缺点:代码复杂度高了

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值