题目:在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果。
解法一:转化成求子数组的最大和
int MaxDiff_Solution2(int numbers[], unsigned length)
{
if(numbers == NULL || length < 2)
return 0;
int* diff = new int[length - 1];
for(int i = 1; i < length; ++i)
diff[i - 1] = numbers[i - 1] - numbers[i];
int currentSum = 0;
int greatestSum = 0x80000000;
for(int i = 0; i < length - 1; ++i)
{
if(currentSum <= 0)
currentSum = diff[i];
else
currentSum += diff[i];
if(currentSum > greatestSum)
greatestSum = currentSum;
}
delete[] diff;
return greatestSum;
}
时间复杂度O(n),空间复杂度O(n).
解法二:分治算法
通常蛮力法不会是最好的解法,我们想办法减少减法的次数。假设我们把数组分成两个子数组,我们其实没有必要拿左边的子数组中较小的数字去和右边的子数组中较大的数字作减法。我们可以想象,数对之差的最大值只有可能是下面三种情况之一:(1)被减数和减数都在第一个子数组中,即第一个子数组中的数对之差的最大值;(2)被减数和减数都在第二个子数组中,即第二个子数组中数对之差的最大值;(3)被减数在第一个子数组中,是第一个子数组的最大值。减数在第二个子数组中,是第二个子数组的最小值。这三个差值的最大者就是整个数组中数对之差的最大值。第(1)(2)问题都是原问题的子问题,而第三个问题可以线性时间解决。
bool invalid=false;
int MaxDiffCore(int *data,int length)
{
if(length<3)
{
return data[0]-data[1];
}
int left_diff=MaxDiffCore(data,length/2);
int right_diff=MaxDiffCore(data+length/2,length-length/2);
int max=data[0],min=data[length/2];
//下面俩次循环取左侧子数组的最大值、右侧子数组的最小值
for(int i=1;i<length/2;i++)
{
if(data[i]>max)
max=data[i];
}
for(int j=length/2+1;j<length;j++)
{
if(data[j]<min)
min=data[j];
}
int diff=max-min;
//返回三值中的最大值
if(left_diff>right_diff)
{
return left_diff>diff?left_diff:diff;
}
else
{
return right_diff>diff?right_diff:diff;
}
}
int MaxDiff_Solution(int *data,int length)
{
if(data==NULL || length<2)
{
invalid=true;
return 0;
}
return MaxDiffCore(data,length);
}
时间复杂度:T(n)=2T(n/2)+O(1)。根据主定理得复杂度O(n)
动态规划是最优的解法,因为没有额外的空间开销,时间复杂度仍旧是O(n),但也是最难分析的一个方法。我们定义diff[i]是以第i个数字为减数的所有数对之差的最大值,也就是说对于任意的h(h<i),diff[i]>=data[h]-data[i]。diff[i]的最大值就是整个数组最大的数对之差。
假设我们已经求得了diff[i],怎么求得diff[i+1]?对于diff[i],肯定存在h(h<i),满足data[h]-data[i]是最大的,也就是说data[h]应该是data[i]左侧所有数字的最大值。当我们求diff[i+1]的时候,我们需要找到第i+1个数字左侧的最大值。有俩种可能:data[i]或者前面的data[h]。取俩这最大值就可以了。怎么感觉和求子数组最大和如此的神似呢!!
int MaxDiff_Solution(int *data, unsigned int length)
{
if(data == NULL || length < 2)
return 0;
int max = data[0];
int maxDiff = max - data[1];
for(int i = 2; i < length; ++i)
{
if(data[i - 1] > max)
max = data[i - 1];
int currentDiff = max - data[i];
if(currentDiff > maxDiff)
maxDiff = currentDiff;
}
return maxDiff;
}