1.7 好玩的约瑟夫环

作者 李卫明

单位 杭州电子科技大学

1.7 好玩的约瑟夫环:有M个人,编号分别为1到M,玩约瑟夫环游戏,最初时按编号顺序排成队列;每遍游戏开始时,有一个正整数报数密码N,队列中人依次围坐成一圈,从队首的人开始报数,报到N的人出列,然后再从出列的下一人开始重新报数,报到N的人出列;重复这一过程,直至所有人出列,完成一遍游戏,所有出列的人形成新队列;游戏可能玩很多遍,每遍有新报数密码。求若干遍游戏完成后队列次序。题目输入包括若干个正整数(至少1个),第一个正整数为玩游戏人数M,后续每个正整数为每遍游戏报数密码,报数密码可能为1,题目要求按出队列顺序输出他们的编号。

输入格式:

正整数M和后面的若干个正整数报数。

输出格式:

每个测试用例结果占一行,每个编号占4位。

输入样例:

10   3   5    2

输出样例:

   4   6   5   2   9   1   3   7   8  10

代码长度限制

16 KB

时间限制

400 ms

内存限制

64 MB

栈限制

8192 KB

C程序如下:

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

typedef struct LinkNode {
    int Data;
    struct LinkNode* Next;
} LinkNode, *LinkList;

LinkList Append(int num); // 构建单向循环链表
void Display(LinkList L); // 输出约瑟夫环
LinkList Newjosephus(LinkList old, int times); // 根据第一次输出的约瑟夫环的值构建约瑟夫环

int main() {
    LinkList first, second;//创建两个指针,分别为第一次构建的约瑟夫环和后序的约瑟夫环
    int n, number;//n为每次需要报的数,number为报数的总人数
    int end[100], i = 0;//定义一个数组用来装每次的报数
    scanf("%d", &number);//输出报数的总人数
    first = Append(number); // 先构建一个数量为number的约瑟夫环

    while (scanf("%d", &n) != EOF) {//先记录每次的报数情况,因为不清楚要报数多少次
        end[i++] = n;
    }

    for (int j = 0; j < i; j++) {//根据前面记录的报数情况再次循环构建新的约瑟夫环
        if (j == 0) {
            second = Newjosephus(first, end[j]); // 构建新的约瑟夫环,第一次单独处理
        } else {
            second = Newjosephus(second, end[j]); // 再次构建约瑟夫环
        }
    }

    Display(second); // 输出最终的约瑟夫环
    return 0;
}

LinkList Append(int num) { // 构建单向循环链表,采用边构建边闭合的方式
    LinkList pHead = NULL, pTail = NULL;//定义头指针和尾指针

    for (int i = 1; i <= num; i++) {//循环报数的总人数次
        LinkList newNode = (LinkList)malloc(sizeof(LinkNode)); // 先创建一个新结点
        newNode->Data = i;
        newNode->Next = NULL;

        if (pHead == NULL) {//如果是第一个结点
            pHead = pTail = newNode; // 让头指针和尾指针都指向该结点
            pHead->Next = pHead; // 闭环
        } else {//如果不是第一个结点
            newNode->Next = pHead; // 先闭环
            pTail->Next = newNode; // 尾插法
            pTail = newNode;
        }
    }

    return pHead; // 返回头指针
}

void Display(LinkList L) { // 输出该约瑟夫环
    LinkList temp = L;

    if (temp == NULL) return;//如果约瑟夫环为空直接返回

    do {
        printf("%4d", temp->Data);//先输出一次再判断是否这个循环链表已经输出完毕
        temp = temp->Next;//继续输出下一个
    } while (temp != L);

    printf("\n");
}

LinkList Newjosephus(LinkList old, int times) { // 根据输出约瑟夫环改造,将输出环节和删除环节改为重新添加到一个单链表当中去
    if (times == 1) { // 如果报数为1,直接输出链表,即让该链表的值原封不动的赋值给新的链表
        LinkList temp = old;//让指针指向上一次构建的约瑟夫环的头
        LinkList pHead = NULL, pTail = NULL;//定义头指针和尾指针

        do {
            LinkList newNode = (LinkList)malloc(sizeof(LinkNode));
            newNode->Data = temp->Data;
            newNode->Next = NULL;

            if (pHead == NULL) {//尾插法构建单向循环链表
                pHead = pTail = newNode;
                pHead->Next = pHead;
            } else {
                newNode->Next = pHead;
                pTail->Next = newNode;
                pTail = newNode;
            }

            LinkList toDelete = temp;//依次删除报数为1的数
            temp = temp->Next;
            free(toDelete);
        } while (temp != old);

        return pHead;//返回头指针
    }

    int count = 0;
    LinkList pHead = NULL, pTail = NULL;

    do {
        count++;

        if (count == times - 1) {
            LinkList pDel = old->Next;
            old->Next = pDel->Next;

            LinkList newNode = (LinkList)malloc(sizeof(LinkNode));
            newNode->Data = pDel->Data;
            newNode->Next = NULL;

            if (pHead == NULL) {
                pHead = pTail = newNode;
                pHead->Next = pHead; // 闭环
            } else {
                newNode->Next = pHead; // 先闭环
                pTail->Next = newNode;
                pTail = newNode;
            }

            free(pDel); // 删除目标报数结点
            count = 0; // 计数器重置为0
        }

        old = old->Next; // 指向下一个结点
    } while (old->Next != old); // 当单链表只剩一个结点时退出循环

    LinkList newNode = (LinkList)malloc(sizeof(LinkNode)); // 再将最后一个结点加入新链表当中去
    newNode->Data = old->Data;
    newNode->Next = pHead; // 先闭环
    pTail->Next = newNode;

    free(old);

    return pHead; // 返回新链表的头结点
}
  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[3\]提到,HashMap在1.7版本中没有使用红黑树,而是使用了链表来存储元素。由于使用尾插法,每次都要遍历链表去获取尾节点,这样会耗费性能。而在1.8版本中,HashMap采用了红黑树结构,并且转换条件是链表长度大于8时才会转换成红黑树。这样,遍历链表的次数最大就是8,当超过8时就会转换成红黑树。遍历红黑树的次数取决于树的深度,所以遍历的性能消耗可以忽略不计。这样就解决了头插法循环引用的问题。\[3\] 所以,在HashMap 1.7版本中,由于没有使用红黑树,可能会存在环的问题。但在1.8版本中,通过使用红黑树结构,并设置了转换阈值为8,可以有效地解决环的问题。 #### 引用[.reference_title] - *1* [HashMap1.7 扩容产生环形链表](https://blog.csdn.net/weixin_42496727/article/details/124118191)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [HashMap的原理,1.7为什么会形成环](https://blog.csdn.net/weixin_41796956/article/details/120562231)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值