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