剑指offer打卡Day13:孩子们的游戏
发布日期:2021-05-07 22:28:41 浏览次数:22 分类:精选文章

本文共 2064 字,大约阅读时间需要 6 分钟。

题1:圆圈中最后剩下的数

题目描述

每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。

HF作为牛客的资深元老,自然也准备了一些小游戏。

其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)

如果没有小朋友,请返回-1

示例

输入

5,3

返回值

3

解析:

  • 从故事中抽取题目,可整理得:

    给定一个由[0…n-1]构成的数组,第一次从0开始数m个数,然后删除,以后每次都从删除的数下一个位置开始数m个数,然后删除,直到剩余一个数字,找出那个数字。

    如图:

    在这里插入图片描述

    在图中我们可以看到,在首次删除掉了(m-1)位元素后,之后都是不断在数组中***循环***遍历第m个数字进行删除。

    • 对于循环的操作,此时可以用到 % (取余)运算

    • 对于%运算的理解:

      • 3%10 = 1;(3/10=3......1)

        举例说明:

        在数组[1,2,3],从第一位元素开始从左往右循环数10次,第10次必定会落在第一位。

      • 数据结构中的循环队列也是运用这一操作(等下题刷到了再仔细讲解)

    • 综上对于本次的操作,我们可以借用链表与%运算求出(见解答一)

    • 不过答案提交之后显示运行时间位30+ms,总感觉太慢了需要优化。

  • 查阅资料得知此题与 十分相似:

    • 约瑟夫问题(有时也称为约瑟夫斯置换,是一个和中的问题。在计算机的中,类似问题又称为约瑟夫环。又称“丢手绢问题”.)

    • 据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。然而Josephus 和他的朋友并不想遵从。首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。接着,再越过k-1个人,并杀掉第k个人。这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。问题是,给定了和,一开始要站在什么地方才能避免被处决。Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

    • 最后可根据约瑟夫问题得出一个公式

      |—— 0                 n=1f(n,m)=|       |—— [f(n-1,m)+m]%n    n>1
    • 具体推导可见博客:

      首先分析可以得知,这是一道约瑟夫环的问题。具体约瑟夫环原理可以参考下面链接。

      约瑟夫环——公式法(递推公式)

      讲解非常详细,必看

解答:

//借用链表:public int LastRemaining_Solution_withLinkedList(int n, int m) {       LinkedList
list = new LinkedList
(); for (int i = 0; i < n; i ++) { list.add(i); } int bt = 0; while (list.size() > 1) { bt = (bt + m - 1) % list.size(); //int num = list.get(bt); list.remove(bt); //System.out.println("移除:"+num+"---->"+list); } return list.size() == 1 ? list.get(0) : -1;}/*打印结果:与推理一致[0, 1, 2, 3, 4, 5]移除:2---->[0, 1, 3, 4, 5]移除:5---->[0, 1, 3, 4]移除:3---->[0, 1, 4]移除:1---->[0, 4]移除:4---->[0]*/
//运用约瑟夫环问题推导公式: public int LastRemaining_Solution(int n, int m) {        if (n <= 0) return -1;     int index = 0;     for (int i=2; i<=n; ++i) {            index = (index + m) % i;     }     return index; }
上一篇:剑指offer打卡Day14:数组中只出现一次的数字
下一篇:剑指offer打卡Day11:左旋转字符串

发表评论

最新留言

逛到本站,mark一下
[***.202.152.39]2025年04月20日 09时08分26秒