约瑟夫环(三种实现,标记、假循环链表、动态规划)

方法一:
用数组下标表示每个人的位置,数组元素0代表环内、1代表出环。

public int lastRemaining(int n, int m) {
        int[] arr = new int[n];
        int num = 0;
        int index = 0;
        int count = n;
        int res = 0;
        while (count > 0) {
            num += (arr[index] == 0 ? 1 : 0);
            if (num >= m) {
                arr[index] = 1;
                num = 0;
                count--;
                res = index;
            }
            index = (index + 1) % n;
        }
        return res;
    }

但这种纯遍历在数据多了后会导致超时。

方法二:
假循环链表:
约瑟夫环简单出环问题可以用链表的改节点指向实现。
但假链表更加方便:依旧用数组存储,但下标表示其指向的下一个元素的下标,遍历时依据元素值遍历。

public int lastRemaining(int n, int m) {
        int[] people = new int[n];
        int count = n;
        int num = 0;
        int res = 0;
//        for (int i = 0; i < n; i++) {
//            people[i] = (i + 1) % n;
//        }
        for (int i = 0; i < n - 1; i++) {
            people[i] = i + 1;
        }

        int curIndex = 0;
        int preIndex = (n - 1 + count) % count;
        while (count > 0) {
            num++;
            if (num >= m) {
                res = curIndex;
                people[preIndex] = people[curIndex];
                num = 0;
                count--;
            } else {
                preIndex = curIndex;
            }
            curIndex = people[curIndex];
        }
        return res;
    }

这种方法在m<n时,时间效率更高,但是当n < m,且数据很大时,仍然超时。

方法三:
用动态规划的思想:
求解f(n),当出队列一个人时,即又求解f(n-1)
此时,设前一个出队人的下标为t
f(n)=(f(n−1)+t)%n
=(f(n−1)+m%n)%n
=(f(n−1)+m)%n

反向推就是,当环内只有一个人,一定是下标为0出队列,之后每次出队列都是在前一个人的(下标上进行+m) % 当前n操作

public int lastRemaining(int n, int m) {
        int x = 0;
        for (int i = 2; i <= n; i++) {
            x = (x + m) % i;
        }
        return x;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

魔幻音

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

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

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

打赏作者

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

抵扣说明:

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

余额充值