面试题45:圆圈中最后剩下的数字

题目:0,1,...,n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。

例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前四个数字一次是2、0、4、1,最后剩下的数字为3.

  本题是有名的约瑟夫环问题。

解法一:经典的解法,用环形链表模拟圆圈

如果面试官没有特殊要求,可以用模板库中的list来模拟一个环形链表,而list本身不是一个环形结构,因此每当迭代器扫描到链表末尾的时候,

要记得把迭代器移到链表的头部。

 1 int lastRemaining(unsigned int n, unsigned int m)
 2 {
 3     if (n < 1 || m < 1)
 4         return -1;
 5     list <int>numbers;
 6     for (int i = 0; i < n; ++i)
 7         numbers.push_back(i);
 8     list<int>::iterator current = numbers.begin();
 9     while (numbers.size() > 1)
10     {
11         for (int j = 1; j < m; ++j)
12         {
13             ++current;
14             if (current == numbers.end())
15                 current = numbers.begin();
16         }
17         current = numbers.erase(current);
18         if (current == numbers.end())
19             current = numbers.begin();
20     }
21     return *current;
22 }

这种思路的时间复杂度为O(MN),空间复杂度为O(N)。

 

解法二:创新的解法

  首先定义一个关于n和m的方程f(n,m),表示每次在n个数字0,1,...,n-1中每次删除第m个数字后最后剩下的数字。经过分析(过程参考《剑指Offer》)得到:

n = 1时,f(n,m) = 0;

n > 1时,f(n,m) = [f(n-1,m)+m]%n;

1 int lastRemaining(unsigned int n, unsigned int m)
2 {
3     if (n < 1 || m < 1)
4         return -1;
5     int last = 0;
6     for (int i = 2; i <= n; ++i)
7         last = (last + m)%n;
8     return last;
9 }

 

posted @ 2015-07-08 11:06  Rosanne  阅读(297)  评论(0编辑  收藏  举报