【剑指】62.圆圈中最后剩下的数字

题目描述

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;
}

猜你喜欢

转载自blog.csdn.net/ansizhong9191/article/details/81482037