题目:在数组中,数字减去它右边的数字得到一个数对之差。求所有数对之差的最大值。例如在数组{2, 4, 1, 16, 7, 5, 11, 9}中,数对之差的最大值是11,是16减去5的结果。
分析:看到这个题目,很多人的第一反应是找到这个数组的最大值和最小值,然后觉得最大值减去最小值就是最终的结果。这种思路忽略了题目中很重要的一点:数对之差是一个数字减去它右边的数字。由于我们无法保证最大值一定位于数组的左边,因此这个思路不管用。
于是我们接下来可以想到让每一个数字逐个减去它右边的所有数字,并通过比较得到数对之差的最大值。由于每个数字需要和它后面的O(n)个数字作减法,因此总的时间复杂度是O(n2)。
解法一:转化成求解子数组的最大和问题
接下来再介绍一种比较巧妙的解法。如果输入一个长度为n的数组numbers,我们先构建一个长度为n-1的辅助数组diff,并且diff[i]等于numbers[i]-numbers[i+1](0<=i<n-1)。如果我们从数组diff中的第i个数字一直累加到第j个数字(j > i),也就是diff[i] + diff[i+1] + … + diff[j] = (numbers[i]-numbers[i+1]) + (numbers[i + 1]-numbers[i+2]) + ... + (numbers[j] – numbers[j + 1]) = numbers[i] – numbers[j + 1]。
分析到这里,我们发现原始数组中最大的数对之差(即numbers[i] – numbers[j + 1])其实是辅助数组diff中最大的连续子数组之和。
代码如下:
// MaxDiff.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
int MaxDiff_Solution2(int numbers[], unsigned length)
{
if(numbers == NULL || length < 2)
return 0;
int* diff = new int[length - 1];//长度为n-1的辅助数组diff
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;
}
int MaxDiff_Solution1(int numbers[], unsigned length)
{
if(numbers == NULL || length < 2)
return 0;
int* diff = new int[length - 1];//长度为n-1的辅助数组diff
for(int i = 1; i < length; ++i)
diff[i - 1] = numbers[i - 1] - numbers[i];
int currentSum = 0;
int greatestSum = 0;
for(int i = 0; i < length - 1; ++i)
{
currentSum+=diff[i];
if (currentSum>greatestSum)
{
greatestSum=currentSum;
}
if (currentSum<0)
{
currentSum=0;
}
}
delete[] diff;
return greatestSum;
}
int _tmain(int argc, _TCHAR* argv[])
{
int a[]={2, 4, 1, 16, 7, 5, 11, 9};
int len =sizeof(a)/sizeof(int);
cout<<MaxDiff_Solution1(a,len)<<endl;
system("pause");
return 0;
}