题目描述
0, 1, …, n-1这n个数字排成一个圆圈,从数字0开始每次从这个圆圈里删除第m个数字。求出这个圆圈里剩下的最后一个数字。
算法分析
方法一:使用链表来模拟圆圈,时间复杂度为O(mn),空间复杂度O(n)。
方法二:约瑟夫环问题的数学公式法求解,公式推导见约瑟夫环问题 ( 最简单的数学解法),得到公式f(n, m)= [f(n-1, m) + m] % n,其中,m表示每次数到该数的人出列,n表示当前序列的总人数。
提交代码:
class Solution {
public:
/* 方法一:模拟环形链表 */
int LastRemaining_Solution1(int n, int m)
{
if (n < 1 || m < 1)
return -1;
list<int> data;
for (int i = 0; i < n; ++i)
data.push_back(i);
auto curr = data.begin();
while (data.size() > 1)
{
// 找到第m个数字
for (int i = 1; i < m; ++i)
{
++curr;
if (curr == data.end())
curr = data.begin();
}
// 保存下一个节点的地址
auto next = ++curr;
if(next == data.end())
next = data.begin();
--curr;
data.erase(curr);
curr = next;
}
return *curr;
}
/* 方法二:数学公式推导 */
int LastRemaining_Solution2(int n, int m)
{
if (n < 1 || m < 1)
return -1;
int last = 0;
for (int i = 1; i <= n; ++i)
last = (last + m) % i;
return last;
}
};
测试代码:
// ====================测试代码====================
void Test(const char* testName, unsigned int n, unsigned int m, int expected)
{
if (testName != nullptr)
printf("%s begins: \n", testName);
Solution s;
if (s.LastRemaining_Solution2(n, m) == expected)
printf("Solution1 passed.\n");
else
printf("Solution1 failed.\n");
printf("\n");
}
void Test1()
{
Test("Test1", 5, 3, 3);
}
void Test2()
{
Test("Test2", 5, 2, 2);
}
void Test3()
{
Test("Test3", 6, 7, 4);
}
void Test4()
{
Test("Test4", 6, 6, 3);
}
void Test5()
{
Test("Test5", 0, 0, -1);
}
void Test6()
{
Test("Test6", 4000, 997, 1027);
}
int main(int argc, char* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
Test6();
return 0;
}