约瑟夫问题Java的三种解决办法

约瑟夫问题分析

约瑟夫问题 : N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。

一、解法一:数组

 public static int joseph2(int m,int k){
        int ans = 0;
        int[] arr = new int[m];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = 1;
        }
        int count = 0,num = 0;
        for (int i = 0; i <= arr.length; i++) {
            if (i == arr.length) {
                i = -1;
                continue;
            }
            if (arr[i] == 1){
                //有效元素
                ++count;
                if (count == k){
                    arr[i] = 0;
                    ++num;//存活数+1
                    count = 0;//重置计数器
                }
            }
            if (num == m-1){
                for (int j = 0; j < arr.length; j++) {
                    if (arr[j] == 1){
                        return j+1;
                    }
                }
            }
        }
        return ans;

    }

二、解法二:链表

构造链表结构,并将链表最后节点指向头结点。
然后通过计数,删除节点,知道最后只剩下一个节点(一个next指针指向自己的节点)。

    static class Node{
       public int value;
       public Node next;

        public Node(int value) {
            this.value = value;
            this.next = null;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    }

    /**
     * 使用链表方法
     * @param m
     * @param k
     * @return
     */
    public static int joseph3(int m,int k){
        Node head = new Node(1);
        Node pre = head;
        for (int i = 2; i <= m; i++) {
            Node cur = new Node(i);
            pre.next = cur;
            pre = cur;
        }
        pre.next = head;//注意此处形成回环结构 最后一个节点 指向 头节点
        Node cur = head;//从头节点开始遍历
        pre = null;
        int count = 1;//计数从1开始
        while (cur.next != cur){
            if (count != k){
                //向下移动
                pre = cur;
                cur = cur.next;
                ++count;
            }else if (count == k){
                //删除节点
                pre.next = cur.next;
                System.out.println("删除节点:"+cur.value);
                cur = cur.next;
                count = 1;//重置计数
            }
        }
        return cur.value;

    }

三、解法三:递推法

递推法分析可参考:约瑟夫环——公式法(递推公式)

 /**
     * 题目是从n个人开始,每次减少一个人,最终只剩下一个人
     * 每减少一个人,胜利者的位置-k,例如:
     * 1 2 3 4 5 6 m=6 k = 5
     * 第一次5被杀掉,6则从1开始,所以 胜利者位置也减掉5
     * 逆向思考:
     * 从1个人开始,那这个人一定是胜利者,然后人数递增,从而推算胜利者的位置
     * 每增加一个人,胜利者的位置+k
     * 所以 假设s代表胜利者的位置,每增加一个人 s = s+k
     * 并且当超过总人数时,从头开始,所以需要对s+k做取余操作
     * 即 s = (s+k)%i,i为当前的人数
     * 并且最终返回的结果要+1,因为数组编号是从0开始
     */
    public static int joseph1(int m ,int k){
        int s = 0;//存活者编号
        for (int i = 1; i <= m ; i++) {
            s = (s+k)%i;
        }
        return s+1;
    }

完整源码

/**
 * @author :LiuShihao
 * @date :Created in 2022/6/18 10:07 下午
 * @desc :约瑟夫问题 : N个人围成一圈,第一个人从1开始报数,报M的将被杀掉,下一个人接着从1开始报。如此反复,最后剩下一个,求最后的胜利者。
 */
public class JosephProblem {

    public static void main(String[] args) {
        System.out.println(joseph1(6,2));
        System.out.println(joseph2(6,2));
        System.out.println(joseph3(6,2));
    }

    /**
     * 题目是从n个人开始,每次减少一个人,最终只剩下一个人
     * 每减少一个人,胜利者的位置-k,例如:
     * 1 2 3 4 5 6 m=6 k = 5
     * 第一次5被杀掉,6则从1开始,所以 胜利者位置也减掉5
     * 逆向思考:
     * 从1个人开始,那这个人一定是胜利者,然后人数递增,从而推算胜利者的位置
     * 每增加一个人,胜利者的位置+k
     * 所以 假设s代表胜利者的位置,每增加一个人 s = s+k
     * 并且当超过总人数时,从头开始,所以需要对s+k做取余操作
     * 即 s = (s+k)%i,i为当前的人数
     * 并且最终返回的结果要+1,因为数组编号是从0开始
     */
    public static int joseph1(int m ,int k){
        int s = 0;//存活者编号
        for (int i = 1; i <= m ; i++) {
            s = (s+k)%i;
        }
        return s+1;
    }
    //数组
    public static int joseph2(int m,int k){
        int ans = 0;
        int[] arr = new int[m];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = 1;
        }
        int count = 0,num = 0;
        for (int i = 0; i <= arr.length; i++) {
            if (i == arr.length) {
                i = -1;
                continue;
            }
            if (arr[i] == 1){
                //有效元素
                ++count;
                if (count == k){
                    arr[i] = 0;
                    ++num;//存活数+1
                    count = 0;//重置计数器
                }
            }
            if (num == m-1){
                for (int j = 0; j < arr.length; j++) {
                    if (arr[j] == 1){
                        return j+1;
                    }
                }
            }
        }
        return ans;

    }

    static class Node{
       public int value;
       public Node next;

        public Node(int value) {
            this.value = value;
            this.next = null;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    }

    /**
     * 使用链表方法
     * @param m
     * @param k
     * @return
     */
    public static int joseph3(int m,int k){
        Node head = new Node(1);
        Node pre = head;
        for (int i = 2; i <= m; i++) {
            Node cur = new Node(i);
            pre.next = cur;
            pre = cur;
        }
        pre.next = head;//注意此处形成回环结构 最后一个节点 指向 头节点
        Node cur = head;//从头节点开始遍历
        pre = null;
        int count = 1;//计数从1开始
        while (cur.next != cur){
            if (count != k){
                //向下移动
                pre = cur;
                cur = cur.next;
                ++count;
            }else if (count == k){
                //删除节点
                pre.next = cur.next;
                System.out.println("删除节点:"+cur.value);
                cur = cur.next;
                count = 1;//重置计数
            }
        }
        return cur.value;

    }

}


版权声明:本文为DreamsArchitects原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。