约瑟夫环问题—双指针链表解决、数组取模解决

num个人围成一圈,从start位置开始报数,每报到step的人就出局,直到剩下最后一人。

约瑟夫环问题,可以使用链表和数组方式解决方式:
链表方式好理解,两个指针跟着走,一步一步报数,出圈,直至两个指针指向同一个位置为止;
数组方式,不需要一步一步跟着报数,直接取模移到需要出圈的位置,数据出圈,构建新的数组,再报数,出圈,直至数组长度为1为止;

区别在于:数组取模方式不用跟着数,对于num较大step较大的场景更省时间;网上还有另外一种直接用数学公式计算的方式,算法更省时间;

双指针链表方式解决 JosephLinked.java

/**
 * 节点
 */
class SignLinkedNode implements Serializable {
    public Integer value;
    //下一个节点
    public SignLinkedNode next;

    public SignLinkedNode() {
    }

    public SignLinkedNode(Integer value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return String.valueOf(value);
    }
}

/**
 * 约瑟夫环问题-链表解法
 * num个人围成一圈,从s开始报数,每报到step的人就出局,直到剩下最后一人
 */
public class JosephLinked {
    //头指针
    SignLinkedNode head;
    //尾指针
    SignLinkedNode current;
    //链表长度
    int size;

    /**
     * 添加节点
     * @param node
     */
    public void add(SignLinkedNode node) {
        if (size == 0) {
            head = node;
            current = node;
        } else {
            current.next = node;
        }
        size++;
        //尾指针指向最后一个元素
        node.next = head;
        //当前指向该元素
        current = node;
    }

    /**
     * 遍历节点
     */
    public void show() {
        SignLinkedNode temp = head;
        do {
            System.out.printf(temp + ", ");
            temp = temp.next;
        } while (temp != head);
        System.out.println();
    }

    /**
     * 移动指针
     * @param start
     * @param step
     */
    public void run(int start, int step) {
        int i = 0;
        //2个指针都移动到起点
        while (i != start) {
            head = head.next;
            current = current.next;
            i++;
        }
        i = 1;//从1开始报数
        //报数到step
        while (i != step) {
            head = head.next;
            current = current.next;
            i++;
        }
        //出局人
        System.out.println("出局人: " + head);
        //移除人
        current.next = head.next;
        head = head.next;
        size--;
        if (current == head) {
            System.out.println("最后出局人: " + head);
            return;
        }
        //起始位置开始数
        run(0, step);
    }

    /**
     * 数组实现
     *
     * @param start 起点
     * @param step  出局报数
     * @return 位置从0开始
     */
    private static int initJoseph(int num, int start, int step) {
        //头结点
        JosephLinked josephLinked = new JosephLinked();
        for (int i = 0; i < num; i++) {
            josephLinked.add(new SignLinkedNode(i + 1));
        }
        //遍历输出
        josephLinked.show();
        //开始报数
        josephLinked.run(start, step);
        return josephLinked.head.value;
    }

    public static void main(String[] args) {
//        System.out.println(initJoseph(523, 24, 79));
        System.out.println(initJoseph(5, 2, 2));
    }
}

数组取模方式解决 JosephArray.java

/**
 * 约瑟夫环问题
 * num个人围成一圈,从s开始报数,每报到step的人就出局,直到剩下最后一人
 */
public class JosephArray {
    /**
     * 数组实现
     *
     * @param arr   人
     * @param start 起点
     * @param step  出局报数
     * @return 位置从0开始
     */
    private static int jose(int[] arr, int start, int step) {
//        System.out.println(Arrays.toString(arr));
        //圈子只剩1人
        if (arr.length == 1) {
            System.out.println("最后出局人: " + arr[0]);
            return arr[0];
        }
        //出局人的位置
        int i = (start + step - 1) % arr.length;
        //出局人
        System.out.println("出局人: " + arr[i]);
        //下一个人起始
        int w = i % (arr.length - 1);
        //新环-缩小一个人
        int[] arr2 = new int[arr.length - 1];
        for (int j = 0, c = 0; j < arr.length; j++) {
            if (j != i) {
                arr2[c++] = arr[j];
            }
        }
        return jose(arr2, w, step);
    }

    private static int initJoseph(int num, int start, int step) {
        //初始化人
        int[] arr = new int[num];
        for (int i = 0; i < num; i++) {
            arr[i] = i + 1;
        }
        return jose(arr, start, step);
    }

    public static void main(String[] args) {
//        System.out.println(initJoseph(523, 24, 79));
        System.out.println(initJoseph(5, 2, 2));
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小绿豆

你的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值