数据结构-循环列表

本文介绍了循环链表这一特殊的链式存储结构,其特点在于最后一个结点的指针指向头结点,形成一个环。操作与单链表类似,但判断循环条件不同。文章还探讨了利用循环链表实现魔术师发牌问题,强调在处理过程中要考虑牌的数量减少,而链表长度不变的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

循环链表是另一种形式的链式存储结构,他的特点是表中最后一个结点的指针域指向头结点,整个链表形成一个环,由此从表中任一结点出发均可找到表中其他结点,其操作与单链表基本一致,差别在于循环条件不是p或p->next是否为空,而是否等于头指针。

循环链表的结点定义,与单链表一样,在生成循环链表是应该,将r->next == head(即最后一个结点指针域指向头结点)

typedef struct node
{
    int data;
    struct node *next;
}sqlist, *linkList;

拉丁方阵:
形如

#include "stdio.h"
#include "stdlib.h"

#define OK 1
#define ERROR 0

typedef int Status;

typedef struct node
{
    int data;
    struct node *next;
} *linkList, sqlist;

//输出数据
Status visit(int e)
{
    printf("%d ", e);
    return OK;
}

//输出整个链表
Status ListTraverse(linkList L)
{
    linkList p = L;
    while(1)
    {
        visit(p->data);
        p = p->next;
        if(p == L)
        {
            return OK;
        }
    }
}

//初始化循环链表
Status InitList(linkList *L)
{
    *L = (linkList)malloc(sizeof(sqlist));
    if(!(*L))
    {
        return ERROR;
    }
    (*L)->next = *L;
    //printf("初始化%d  %d", *L, (*L)->next);
    return OK;
}

//生成循环链表
Status CreateList(linkList *L, int i)
{
    linkList p, r;
    int k=1;
    *L = (linkList)malloc(sizeof(sqlist));
    (*L)->next = NULL;
    r = *L;
    (*L)->data = k;
    //printf("(*L)元素值:%d", (*L)->data);
    for(k=2; k<=i; k++)
    {
        p=(linkList)malloc(sizeof(sqlist));
        p->data = k;
        //printf("元素值:%d", p->data);
        r->next = p;
        r = p;
    }
    p->next = *L;
    return OK;
}

//循环链表长度
int ListLength(linkList L)
{
    linkList p = L;
    int i=0;
    while(1)
    {
        p = p->next;
        i ++;
        if(p==L)
            return i;
    }
}

//拉丁方阵输出
Status LatinOut(linkList L)
{
    int i = ListLength(L);
    int j;
    linkList p, r;
    p = L;
    for(j=0; j<i; j++)
    {
        ListTraverse(p);
        printf("\n");
        r = p->next;
        p = r;//交换p与r的位置,使p->next成为新链表的起点
    }
    return OK;
}

int main(void)
{
    linkList L;
    int i;
    InitList(&L);
    printf("\n请输入数字(1-9):");
    scanf("%d", &i);
    CreateList(&L, i);

    printf("按如下排列:\n");
    ListTraverse(L);
    printf("\n\n");
    printf("拉丁方阵:\n");
    LatinOut(L);

    return 0;
}

魔术师发牌:魔术师利用已排好顺序的13张黑桃牌,第一张翻开黑桃A,放到桌子上,然后继续,数1,2将第一张放到牌的底部,第二张刚好是黑桃2,放到桌面,然后以此将所有牌翻出。所以魔术师的牌顺序如何放置呢?

#define CardNumber 13

//创建循环链表
linkList CreateLinkList()
{
    linkList head = NULL;
    linkList s, r;
    int i;
    r = head;
    for(i=1; i<=CardNumber; i++)
    {
        s = (linkList)malloc(sizeof(sqlist));
        s->data = 0;

        if(head == NULL)
            head = s;
        else
            r->next = s;

        r = s;
    }
    r->next = head;
    return head;
}

void Magician(linkList head)
{
    linkList p;
    int j;
    int CountNumber = 2;
    p = head;
    p->data = 1; //第一张牌
    while(1)
    {
        for(j=0; j<CountNumber; j++)
        {
            p = p->next;
            if(p->data != 0)
            {
                p->next;
                j--;
            }
        }
        if(p->data == 0)
        {
            p->data = CountNumber;
            CountNumber ++;
            if(CountNumber == 14)
                break;
        }
    }
}

void DestroyList(linkList *list)
{
    linkList ptr = *list;
    linkList buff[CardNumber];
    int i = 0;
    while(i < CardNumber)
    {
        buff[i++] = ptr;
        ptr = ptr->next;
    }
    for(i=0; i<CardNumber; ++i)
        free(buff[i]);
    *list = 0;
}

int main(void)
{
    linkList p;
    int i;

    p = CreateLinkList();
    Magician(p);

    printf("按如下排列:\n");
    for(i=0; i<CardNumber; i++)
    {
        printf("黑桃%d ", p->data);
        p = p->next;
    }
    DestroyList(&p);

    return 0;
}

利用循环列表进行魔术师发牌,必须考虑一个很现实的问题,每翻出一张牌,要将其放到桌面,牌堆里就会少一张牌,而循环链表长度总为13,并且里面的元素不变。所以排顺序时要按照,事先已将翻出来的牌“剔除”,即:

    if(p->data != 0) //访问到的结点已有元素,就“跳过”
    {
         p->next;
         j--;
    }

牌放置

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值