题目难度:中等
题目考察:约瑟夫环,链表
题目内容:每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
题目分析:
首先这题要每次删除一个数,可以使用数组或者链表解决这一题,数组可以O(1)查找 O(n)删除,链表可以O(1)删除,O(n)查找,果然,鱼和熊掌不可兼得(块状链表貌似可以兼得hh)但是这题实际上是约瑟夫环问题,我看题解上没有写的很清楚的。。。
首先先给出链表的做法
算法1:链表
class Solution { public: int LastRemaining_Solution(int n, int m) { if (n <= 0) return -1; //人数为0 list<int> lt; //直接用stl里面封装好的链表即可 for (int i=0; i<n; ++i) lt.push_back(i); 放入n个人 int index="0;" 要删除的人 while (n> 1) { index = (index + m - 1) % n; //n个人,从index开始报数,报到m-1的人 auto it = lt.begin(); std::advance(it, index); //该函数可以将it向后移动index个位置 lt.erase(it); //删除链表元素 --n; } return lt.back(); } };
很经典的链表的使用不在细说,下面来说一说约瑟夫环问题
实际上这题就是约瑟夫环问题
理解了上面,这就很简单了
算法2:递推
class Solution { public: int LastRemaining_Solution(int n, int m) { if( n <= 0 || m <= 0 ) return -1; int ans = 0 ; //最后一个删除的肯定是0 for(int i = 2; i <= n ; i++){ ans = ( ans + m ) % i; //这一次删除的数=(上一次删除的数+m)%(上一次剩的人数) } return ans; } };
时间复杂度O(n)
空间复杂度O(1)
代码非常优美简洁,效率十分高