浪潮笔试之搬石头

搬石头排序

题目:沙滩摆放着一排大小不一的球形石头,已知第i个石头的半径为ri,不存在两个石头半径相等。现要求通过移动石头使摆放的石头从左往右半径递增。每次可选择一块石头,并把它放在剩下n-1块石头的最左边或最右边。求最少操作次数。

输入:第一行一个整数n,表示石头个数。(1 <= n <= 100000).第二行n个整数,表示从左往右石头的半径r1,r2,…,rn( 1<= ri <= n)。保证不存在两个不同的石头半径相等。
输出:最少操作次数。
样例输入
6
3 2 1 4 6 5
样例输出
3

分析

实则题目就等于求出最长升序子字符串,但因为题目加了限制条件:每次可选择一块石头,并把它放在剩下n-1块石头的最左边或最右边。所以转变为需要我们去求出最长真升序子字符串,即相对位置与排序完的字符串对应的字符串。比如3 2 1 4 6 5,排完序后:1 2 3 4 5 6,对比两个子字符串,发现原字符串3 4 5排完序相对位置没变,所以3 4 5即为原字符串的最长真升序字符数组。找到最长真升序字符数组后,剩下有多少个其他字符就应该挪动多少次。

参考代码

int MinMoveStone(int n, vector<int> &num)
{
     
	if (n <= 0) return -1; //边界值判定
	vector<int> oderedNum = num;
	sort(oderedNum.begin(), oderedNum.end());
	int maxStrLen = -1; //设置初始值
	int curr = 1; //如果未找到相应真升序子字符串,则为任何某字符本身
	auto itPre = oderedNum.begin();
	auto it = itPre + 1;
	int m = -1; int k = -1;
	while (it != oderedNum.end())
	{
     
		for (int i = 0; i < n; ++i)
		{
     
			if (num[i] == *itPre)
				m = i;
			if (num[i] == *it)
				k = i;
			if (m != -1 && k != -1)
				break;
		}
		if (m < k) //判断是否满足真升序字符串条件
			++curr;
		if (curr > maxStrLen)
			maxStrLen = curr;
		m = -1;
		k = -1;
		++itPre;
		++it;
	}
	return n - maxStrLen; //返回剩下的字符个数
}
时间复杂度:遍历字符数组消耗o(n),由于每次需要找到两个字符的对应下标,所以还需花费o(n)时间,总时间复杂度为o(n^2)。
空间复杂度:用了额外的排序后数组来找寻真升序子字符串,总空间复杂度为o(n)。
改进:可以考虑用Hash存取原数组下标,这样时间复杂度降为:o(n),空间复杂度还是:o(n)。

你可能感兴趣的:(面试题,数据结构,面试,算法)