约瑟夫环问题解决

问题描述:编号为1-N的N个士兵围坐在一起形成一个圈,从编号为1的士兵依次开始报数(1,2,3..依次报),数到m的士兵会被杀死出列,之后的士兵再从1开始报数。直到最后剩下一士兵。求这个士兵的编号。

方法一:数组

第一次做这个题的时候,第一反应就是用数组来做。创建一个boolean数组,用true表示没出列,false表示出列,定义一个计数器,遍历数组,当加到与m相等时,就将true变为false,计数器变为零,重新计数。

public class Test {
    private static Integer compute(Integer n, Integer m) {
        Boolean[] peopleFlags = new Boolean[n];
        for (int i = 0; i < n; i++) {
            peopleFlags[i] = true;
        }
        int peopleLeft = n; //剩下的人数
        int count = 0; //计数器,每过一个人加一,加到m时归零
        int index = 0; //开始下标

        while (peopleLeft > 1) {
            if (peopleFlags[index]) {
                count++;

                if (count == m) {
                    count = 0;
                    peopleFlags[index] = false;
                    peopleLeft--;
                }
            }
            index++;

            //当当前人等于总人数时,则又从第一人开始计数
            if (index == n) {
                index = 0;
            }
        }

        for (int j = 0; j < n; j++) {
            if (peopleFlags[j]) {
                return j + 1;
            }
        }
        return null;
    }


    public static void main(String[] args) {
        int n = 52;
        int m = 3;
        Integer winner = compute(n, m);
        System.out.println(winner);
    }
}

方法二:链表

先实现一个循环链表,在把编号添加到链表里面,也需要一个计数器,初始值为1,遍历链表,当计数器的值等于m是,计数器变为1,删除当下节点。依次直到循环结束。

public class Test2 {

    static class Node{
        int date;
        Node next;

        public Node(int date){
            this.date = date;
        }
    }


    public static int solve(int n,int m){
        if(m == 1 || n<2){
            return n;
        }
        Node head = createLinkedList(n);
        int count = 1;
        Node cur = head;
        Node pre = null;
        while(true){
            //当剩下两个节点时,不能用pre.next = cur.next,会死循环
            if(cur.next == pre && m == 3){
                pre = cur.next;
                break;
            }
            if(count == m){
                count = 1;
                pre.next = cur.next;
                cur = pre.next;
            }else{
                count++;
                pre = cur;
                cur = cur.next;
            }
        }
        return pre.date;
    }

    public static Node createLinkedList(int n){
        Node head = new Node(1);
        Node next = head;
        for(int i = 2;i<=n;i++){
            Node tmp = new Node(i);
            next.next = tmp;
            next = next.next;
        }
        //头尾串联
        next.next = head;
        return head;
    }

    public static void main(String[] args) {
        int a = solve(52,3);
        System.out.println(a);
    }

}

方法三:递归

每次在让一个士兵出列后,对剩下的重新编号。

删除前的编号和删除后编号对比

m-2                           n-2

m-1                           n-1

m                              已删除

m+1                          1

m+2                          2

这样我们就可以得到删除前的编号与删除后的编号的关系:删除前编号 = (删除后编号+m-1)%n+1,为什么不是删除前编号 = (删除后编号+m)%n,因为编号是从1开始的,拖过删除后的编号+m = n,那么删除前的结果就为0,不符合。

public class Test3 {

    public static int f(int n,int m){
        if(n == 1){
            return 1;
        }

        return (f(n-1,m) + m-1) % n +1;
    }

    public static void main(String[] args) {
        System.out.println(f(52,3));
    }
}

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值