剑指Offer-孩子们的游戏(圆圈中最后剩下的数)

package Other;

import java.util.LinkedList;

/**
 * 孩子们的游戏(圆圈中最后剩下的数)
 * 每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。
 * 其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。
 * 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,
 * 继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。
 * 请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
 * 思路:
 * 约瑟夫环问题
 */
public class Solution30 {
    /**
     * 记录一下其中映射关系的由来:
     * n个人排成一排,编号如下:
     * 0,1,2,3,4,.....,n-2,n-1
     * 按照规定,第一个出队的人的编号是 k = (m-1)%n,(对n取余是考虑到m>n的情况),记最后剩下的人的编号为 F(n, m)
     * 接下来从编号为k+1的人开始报数,相当于如下的编号排列:
     * k+1, k+2, k+3, ...., n-2, n-1, 0, 1, ..., k-2, k-1  ////(*)
     * 对应的映射为:
     * 0, 1, 2, .....,n-k-3, n-k-2, n-k-1, n-k, ...., n-3, n-2  /////(**)
     * 描述这个映射关系的函数为:
     * p(x) = (x+n-k-1)%n, 这里的变量x表示上面数列(*)的元素,p(x) 表示上面数列(**)中相同位置对应的值
     * 然后我们实际上需要的映射是描述 (**)--->(*) 的函数,即为p^(x)
     * 如果找到这样的映射p^(x),则F(n-1,m)可以通过p^(x)映射到(*)中,即跟F(n, m)建立联系。
     * 因为p(x) = (x+n-k-1)%n,根据取余运算的定义,它等价于 x+n-k-1 = g*n + p(x),其中g表示某个整数
     * 等式左边移位化简后得:x = (g-1)*n + p(x) + k + 1
     * 注意x代表的是上面数列(*)中的编号,因此x的取值范围为[0,n-1],因此上面的等式需要对n取余,即:
     * x = [(g-1)*n + p(x) + k + 1]%n = [p(x) + k + 1]%n
     * 改写因变量和自变量得到:p^(x) = [x+k+1]%n
     * 上式中的x代表(**)数列中的元素,也即F(n-1,m),将 x = F(n-1, m) 和 k = (m-1)%n 代入即得:
     * F(n, m) = [ F(n-1, m) + (m-1)%n + 1 ]%n
     * 继续化简上式: 由 k=(m-1)%n 得 m-1=j*n+k,得 k = m-1-j*n = (m-1)%n,代入上式:
     * F(n, m) = [ F(n-1, m) + m-1-j*n + 1 ]%n = [ F(n-1, m) + m ]%n
     * 注意 [-j*n]%n = 0
     * 因此递归关系表达式为:F(n, m) = [F(n-1, m)+m]%n 
     */
    public int LastRemaining_Solution_2(int n, int m) {
        if (n < 1 || m < 1) {
            return -1;
        }
        if (n == 1) {
            return 0;
        }
        return ((LastRemaining_Solution_2(n - 1, m) + m) % n);
    }

    /**
     * LinkedList增删节点效率比较高
     *
     * @param n
     * @param m
     * @return
     */
    public int LastRemaining_Solution(int n, int m) {
        if (n < 1 || m < 1) {
            return -1;
        }
        LinkedList<Integer> linkedList = new LinkedList();

        for (int i = 0; i < n; i++) {
            linkedList.add(i);
        }

        int temp = 0;
        while (linkedList.size() > 1) {
            temp = (temp + m - 1) % linkedList.size();//temp为每次出列的人
            linkedList.remove(temp);
        }
        return linkedList.size() == 1 ? linkedList.get(0) : -1;
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏木子昭的博客

机器学习三剑客之PandasPandas的两大核心数据结构Panda数据读取(以csv为例)数据处理Pandas的分组和聚合(重要)

Pandas是基于Numpy开发出的,专门用于数据分析的开源Python库 Pandas的两大核心数据结构 Series(一维数据) 允许索引重复...

34260
来自专栏锦小年的博客

复杂网络(1)--图论的基本理论

1 图论的基本概念 1.1 图(graph)及其分类 (1) 图的定义:图是由点集V={vi}以及V中元素无序对的集合E={ek}所构成的二元组,记为G=(...

287100
来自专栏数据结构与算法

2018.7.30考试

题意:给出两棵树,问在两棵树中任意删除一条边后$1$号节点所在集合的元素相同的方案

12750
来自专栏数据结构与算法

Day4晚笔记

数据结构 并查集:捆绑两个点的信息,判断对错 倍增:LCA, 字符串 hash,模拟, 最小表示法 给定一个环状字符串,切开,使得字符串的字典序最小 图和树 割...

27040
来自专栏数据结构与算法

BZOJ2535: [Noi2010]Plane 航空管制2(拓扑排序 贪心)

首先不难想到拓扑排序,但是直接对原图按\(k\)从小到大拓扑排序是错的。因为当前的\(k\)大并不意味着后面的点\(k\)也大

10630
来自专栏数据结构与算法

HDU 3078 Network

Problem Description The ALPC company is now working on his own network system,...

35870
来自专栏开发与安全

平衡二叉树 AVL 的插入节点后旋转方法分析

平衡二叉树 AVL( 发明者为Adel'son-Vel'skii 和 Landis)是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。 首先我...

26300
来自专栏算法channel

图算法|Prim算法求最小生成树

01 — 一个实际问题 要在n个城市之间铺设光缆,要求有2个: 这 n 个城市的任意两个之间都可以通信; 铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,...

1.1K70
来自专栏数据结构与算法

2455 繁忙的都市

题目描述 Description        城市C是一个非常繁忙的大都市,城市中的道路十分的拥挤,于是市长决定对其中的道路进行改造。城市C的道路是这样分布的...

39990
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

SSE图像算法优化系列九:灵活运用SIMD指令16倍提升Sobel边缘检测的速度(4000*3000的24位图像时间由480ms降低到30ms)。

  这半年多时间,基本都在折腾一些基本的优化,有很多都是十几年前的技术了,从随大流的角度来考虑,研究这些东西在很多人看来是浪费时间了,即不能赚钱,也对工作能力提...

68290

扫码关注云+社区

领取腾讯云代金券