程序员面试金典——解题总结: 9.17中等难题 17.6给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意:n - m越小越好,也就是说,找出

#include 
#include 
#include 
using namespace std;

/*
问题:给定一个整数数组,编写一个函数,找出索引m和n,只要将m和n之间的元素排好序,整个数组就是有序的。注意:n - m越小越好,也就是说,找出
      符合条件的最短序列。
分析:我们将数组分成3个部分,A[0... m-1],A[m ... n],A[n+1...A.length-1]
      那么必定要符合如下条件:前面数组的最大值要<=中间数组的最小值,中间数组的最大值要<=后面数组的最小值
	  如果数组已经有序,那么m,n都设置为-1
	  如果数组无序,可以遍历m和n,需要不断从中间数组中取出最小值和最大值并比较,符合条件就可以
	  时间复杂度= 不断尝试m和n的时间O(n^2) + 从中间数组选择最小值和最大值的的时间O(n)【可以采用线性时间选择算法,具体的算法我记得好像是数组不断划分为5的等分来做】
	  而且需要前面数组和后面数组都要有序
	  
输入:
9(数组元素个数,接下来一行是n个元素)
1 3 5 9 8 7 4 6 13

13
1 2 4 7 10 11 7 12 6 7 16 18 19
输出:
2(m) 7(n)
3 9

书上解法:
1 从左到右遍历,如果当前元素比后面元素大,记录当前元素位置low【牛逼,确定了左边有序序列】
  从右到左遍历,如果当前元素比其前面元素小,记录该元素位置high
  中间数组在下标low+1~high-1
  寻找中间数组中最小值min,最大值max,
  从low位置向前遍历,如果有元素的值 < min,记录其位置pos,则m = pos + 1,
  从high位置向后遍历,如果有元素的值 > max,记录位置pos,则n= pos - 1;表明该元素前面部分都是<=max的,这是不符合题目要求的,所以设定n=pos-1

  【这个解法是错误的】
  例如:1 3 5 9 8 7 4 6 13,
  寻找中间数组得到:[1 3 5 9] [8 7] [4 6 13]
  对中间数组,确定左边中小于中间数组最小值,确定右边大于中间数组最大值,得:[1 2 5] [9 8 7 4 6] [13]
  发现尽管对中间排好序,但是 5 大于 4,仍然不是有序的
  如果继续对数组:                           [1 2 5] [9 8 7 4 6] [13]重复上述处理,找到[9 8 7 4 6]中最小值为4,最大值为9,
 发现修改左边下标为2,把5包含进来,得到最终 [1 2] [5 9 8 7 4 6] [13]   
 结束处理的必要条件必须满足:
  A[beginIndex - 1] <= min ,并且A[endIndex + 1] >= max

2
	//书上解法错误,这里必须要判断找到的下标是否真的还符合题目要求;如果不符合,需要继续寻找下标
	int flag = isCanFinish(datas , beginIndex , endIndex);
	while( 1 !=  flag)
	{
		//如果左边大于中间数组最小值,左边寻找到的下标减1,并继续判断
		if( 2 == flag)
		{
			beginIndex--;
			flag = isCanFinish(datas , beginIndex , endIndex);
		}
		else if( 3 == flag)
		{
			endIndex++;
			flag = isCanFinish(datas , beginIndex , endIndex);
		}
	}

3
寻找数组 datas[low ... high]中的最小值和最大值,这里用分治法来做
分治法原理:将规模为n的子问题划分为k个规模较小的子问题,子问题独立,对子问题求解,并将答案合并
分治法本质是一种特殊地递归,一般可以通过二分法进行处理
分治法处理步骤:1)划分 , 2)处理 ,3)归并答案
如果low = high,表明只有一个元素,则最大值和最小值都是它
否则,令min = datas[low],max=datas[max];
        令middle = low + (high - low)/2;
		得到 result1 = findMinAndMax(datas , low , middle);
		     result2 = findMinAndMax(datas , middle+1 , high);
			 min = result1.min < result2.min ? result1.min : result2.min;
			 max = result1.max > result2.max ? result1.max : result2.max;
*/

bool isOrder(vector& datas)
{
	if(datas.empty())
	{
		return true;
	}
	int size = datas.size();
	for(int i = 1 ; i < size ; i++)
	{
		if(datas.at(i) < datas.at(i-1))
		{
			return false;
		}
	}
	return true;
}

//寻找左边递增序列的截止位置
int findLeftIndex(vector& datas)
{
	if(datas.empty())
	{
		return -1;
	}
	int size = datas.size();
	int i;
	for(i = 0 ; i < size - 1 ; i++)
	{
		if(datas.at(i) > datas.at(i+1))
		{
			return i;
		}
	}
	return i;
}

//寻找右边递增序列的截止位置
int findRightIndex(vector& datas)
{
	if(datas.empty())
	{
		return -1;
	}
	int size = datas.size();
	int i;
	for(i = size - 1 ; i >= 1 ; i--)
	{
		if(datas.at(i) < datas.at(i-1))
		{
			return i;
		}
	}
	return i;
}

//寻找大于或小于value的下标
int findIndex(vector& datas , int index , int value , bool isFindLeft)
{
	//如果是寻找左边数组中,第一个比value小的下标
	int i;
	int size = datas.size();
	if(isFindLeft)
	{
		for(i = index ; i >= 0 ; i--)
		{
			if(datas.at(i) <= value)
			{
				return (i + 1);
			}
		}
	}
	else
	{
		for(i = index ; i < size ; i++ )
		{
			if(datas.at(i) >= value)
			{
				return (i - 1);
			}
		}
	}
	return -1;
}

/*寻找数组 datas[low ... high]中的最小值和最大值,这里用分治法来做
分治法原理:将规模为n的子问题划分为k个规模较小的子问题,子问题独立,对子问题求解,并将答案合并
分治法本质是一种特殊地递归,一般可以通过二分法进行处理
分治法处理步骤:1)划分 , 2)处理 ,3)归并答案
如果low = high,表明只有一个元素,则最大值和最小值都是它
否则,令min = datas[low],max=datas[max];
        令middle = low + (high - low)/2;
		得到 result1 = findMinAndMax(datas , low , middle);
		     result2 = findMinAndMax(datas , middle+1 , high);
			 min = result1.min < result2.min ? result1.min : result2.min;
			 max = result1.max > result2.max ? result1.max : result2.max;
*/
pair findMinAndMax(vector& datas , int low , int high)
{
	int size = datas.size();
	if(datas.empty() || low > high || high >= size)
	{
		pair result(-1,-1);
		return result;
	}
	//如果只有一个元素
	if(low == high)
	{
		pair result(datas.at(low) , datas.at(low));
		return result;
	}
	//如果有两个元素,注意是等于号
	else if( high == low + 1)
	{
		int min = datas.at(low) < datas.at(high) ? datas.at(low) : datas.at(high);
		int max = datas.at(low) > datas.at(high) ? datas.at(low) : datas.at(high);
		pair result(min ,max);
		return result;
	}
	//多个元素,二分后递归处理
	{
		int realMin = INT_MAX;
		int realMax = INT_MIN;
		int middle = low + (high - low) / 2;
		pair result1 = findMinAndMax(datas , low , middle);
		pair result2 = findMinAndMax(datas , middle + 1 , high);
		//比较
		realMin = result1.first < result2.first ? result1.first : result2.first;
		realMax = result1.second > result2.second ? result1.second : result2.second;
		pair result(realMin , realMax);
		return result;
	}
}

//判断当前数组是否可以结束处理,即已经找到正确的下标,返回1:表示可以正确,返回2:表示左边不正确,返回3:表示右边不正确
int isCanFinish(vector& datas , int beginIndex , int endIndex)
{
	if(datas.empty() || beginIndex < 0 || endIndex >= datas.size() || beginIndex >= endIndex)
	{
		return 1;
	}
	pair result = findMinAndMax(datas , beginIndex , endIndex);
	int min = result.first;
	int max = result.second;
	//如果左边大于中间数组最小值
	if(datas.at(beginIndex - 1) > min )
	{
		return 2;
	}
	//如果右边小于中间数组最大值
	else if(datas.at(endIndex + 1) < max)
	{
		return 3;
	}
	else
	{
		return 1;
	}
}

pair getIndexs(vector& datas)
{
	pair invalidResult(-1 , -1);
	if(datas.empty())
	{
		return invalidResult;
	}
	//检查数组是否有序,如果有序,直接返回
	if(isOrder(datas))
	{
		return invalidResult;
	}
	int leftIndex = findLeftIndex(datas);
	int rightIndex = findRightIndex(datas);
	pair result = findMinAndMax(datas , leftIndex + 1 , rightIndex - 1);
	int min = result.first;
	int max = result.second;
	int beginIndex = findIndex(datas , leftIndex , min , true) ;
	int endIndex = findIndex(datas , rightIndex , max , false);
	//书上解法错误,这里必须要判断找到的下标是否真的还符合题目要求;如果不符合,需要继续寻找下标
	int flag = isCanFinish(datas , beginIndex , endIndex);
	while( 1 !=  flag)
	{
		//如果左边大于中间数组最小值,左边寻找到的下标减1,并继续判断
		if( 2 == flag)
		{
			beginIndex--;
			flag = isCanFinish(datas , beginIndex , endIndex);
		}
		else if( 3 == flag)
		{
			endIndex++;
			flag = isCanFinish(datas , beginIndex , endIndex);
		}
	}
	pair realResult(beginIndex , endIndex);
	return realResult;
}

void process()
{
	int num;
	vector datas;
	int value;
	pair result;
	while(cin >> num)
	{
		datas.clear();//复用之前容器,数据要清空
		for(int i = 0 ; i < num ; i++)
		{
			cin >> value;
			datas.push_back(value);
		}
		result = getIndexs(datas);
		cout << result.first << " " << result.second << endl;
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

你可能感兴趣的:(程序员面试金典)