首页 > 试题广场 >

环形链表的约瑟夫问题(进阶)

[编程题]环形链表的约瑟夫问题(进阶)
  • 热度指数:1928 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
据说著名犹太历史学家 Josephus 有过以下故事:在罗马人占领乔塔帕特后,39 个犹太人与 Josephus 及他的朋友躲到一个洞中,39 个犹太人决定宁愿死也不要被敌人抓到,于是决定了一种自杀方式,41 个人排成一个圆圈,由第 1 个人开始报数,报数到 3 的人就自杀,然后再由下一个人重新报 1,报数到 3 的人再自杀,这样依次下去,直到剩下最后一个人时,那个人可以自由选择自己的命运。这就是著名的约瑟夫问题。现在请用单向环形链表得出最终存活的人的编号

输入描述:
一行两个整数 n,m,n 表示链表的长度,m 表示每报数到 m 就自杀。


输出描述:
输出最后存活的人的编号(编号从 1 开始到 n)。
示例1

输入

5 2

输出

3

备注:
刚学的单向环形链表解决约瑟夫问题,结果是符合,就是超时了
但是在本地跑n=500万,秒出的
您的代码已保存
运行超时:您的程序未能在规定时间内运行结束,请检查是否循环有错或算法复杂度过大。
case通过率为28.57%

import java.util.Scanner;
// 一行两个整数 n,m,n 表示链表的长度,m 表示每报数到 m 就***。
public class Main {
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        
       
        Person killed = null;    // ***的人
        Person aheadKilled = null;    // ***之前的人,因为是单项环形链,要想形成链就得知道最后一个人,也就是***的人之前的那个人
        Person newPerson; // 临时变量,新Person,放在循环之外
        // 先组成一个链表
        // 创建N个结点,形成单项环形链
        for (int i = 1; i <= n; i ++) {
            newPerson = new Person(i);
            // 如果是第一个人,那么自己和自己形成环形链
            if (killed == null) {
                killed = newPerson;    // 默认***的就是第一个人
                aheadKilled = newPerson;    // ***之前的那一个人也是自己,因为就剩一个人了
                killed.next = newPerson;    // 自己的下一个人指向自己
            } else {    // 如果不是第一个人,则在末尾处添加新的人
                aheadKilled.next = newPerson;    // 原***之前的那一个人的后一个人指向新Person
                aheadKilled = newPerson;    // 新的***之前的那一个人指向新的Person
                newPerson.next = killed;    // 新Person的后一个人指向***的人
            }
        }
        
        // 如果环内所剩之人一直大于1人,那么一直查数
        // killed与aheadKilled指针相同时,证明环内只剩一人
        while (killed != aheadKilled) {
            // 每报数到m就***,从1开始数,killed实际上移动了m-1次,例如每报数3就***,那么killed指针移动了3-1次,即1->2,2->3共2次
            // 相对应的,***之前的一个人也移动m-1次
            for (int i = 0; i < m - 1; i ++) {
                killed = killed.next;
                aheadKilled = aheadKilled.next;
            }
            // 移动完毕之后,此时killed指针指向的Person即为***之人,将此人从环中除去,即killed后移一位
            killed = killed.next;
            // 重新形成环
            aheadKilled.next = killed;
        }
        
        // 此时最后剩下的killed或aheadKilled指向的Person即为最后剩余之人
        System.out.print(killed.number);
    }
}

class Person {
    
    public Person(int number) {
        this.number = number;
    }
    
    int number;   // 编号 
    Person next;    // 下一个人
}
编辑于 2020-02-01 17:39:58 回复(1)