一、题目描述
给定一个整型数组,找出最大下标距离j-i,当且仅当a[j]>a[i],i < j。
二、直观方案( 时间复杂度为O(n^2) )
对每个元素,从其后找出比其大的元素,并计算下标距离,取距离中的最大值即可。该
方案的时间复杂度为O(n^2)。那么能不能优化下呢?
三、优化方案( 时间复杂度为O(n) )
存在这样一个事实,当a[i+1]大于a[i]时,如果在它们的后面存在一个大于它们的元素a[k],k > i+1>i 那么我们就不需要考虑a[i+1],因为明显的k-i > k-(i+1)。举个例子,数组{3,4, 1, 2,7 },因为3>4,所以元素7与3的下标距离大于7与4的。
基于以上的事实,我们只要从数组的第一个元素开始,找一个下降的序列,从尾部开始扫描,求出最大下标距离。
例如,数组{5, 3, 4, 0, 1, 4, 1},首先找到下降序列{5, 3, 0 },然后依据以下步骤找出答案:
第一步,i = 3,j = 6,a[3] = 0 < a[6] = 1,nMaxSubDistance = 3;
第二步,i = 1,j = 6,a[1] = 3 > a[6] = 1;
第三步,i = 1,j = 5,a[1] = 3 < a[5] = 4,nMaxSubDistance = 3;
第四步,i = 0,j = 5,a[0] = 5 > a[5] = 4;
第五步,i = 0,j = 4,a[0] = 5 > a[4] = 1;
第六步,i = 0,j = 3,a[0] = 5 > a[3] = 0;
第七步,i = 0,j = 2,a[0] = 5 > a[2] = 4;
第八步,i = 0,j = 1,a[0] = 5 > a[1] = 3;
第九步,i = -1,结束;
注意,每次找到一个最大值后,j不需要重置,继续从原来j的位置扫描下去,如果每次j都重置,那算法复杂度就回到O(n^2)了。
代码实现如下:
//返回数组中最大下标距离j-i,当且仅当nArray[i]<nArray[j],j>i
int MaxSubDistance( int nArray[], int nCount )
{
//查找一个下降序列
bool* bDescSeq = new bool[nCount];
memset( bDescSeq, 0, sizeof(bool)*nCount );
int nMinNum = nArray[0];
for ( int i = 1; i < nCount; ++i )
{
if ( nArray[i] < nMinNum )
{
bDescSeq[i] = true;
nMinNum = nArray[i];
}
}
int nMaxSubDistance = 0;
int i = nCount - 1;
int j = nCount - 1;
while( i >= 0 )
{
if( !bDescSeq[i] )
{
--i;
continue;
}
while( j > i && nArray[i] >= nArray[j] )
--j;
if ( (j - i) > nMaxSubDistance )
{
nMaxSubDistance = j - i;
//j = nCount - 1;//这句多余,这句加上的话,时间复杂度则为O(n^2)了。
}
--i;
}
if ( NULL != bDescSeq )
{
delete[] bDescSeq;
bDescSeq = NULL;
}
return nMaxSubDistance;
}
系列文章说明:
1.本系列文章[算法练习],仅仅是本人学习过程的一个记录以及自我激励,没有什么说教的意思。如果能给读者带来些许知识及感悟,那是我的荣幸。
2.本系列文章是本人学习陈东锋老师《进军硅谷,程序员面试揭秘》一书而写的一些心得体会,文章大多数观点均来自此书,特此说明!
3.文章之中,难免有诸多的错误与不足,欢迎读者批评指正,谢谢.