在O(N)时间内求解 正数数组中 两个数相加的 最大值
一,问题描述
给定一个正数数组arr(即数组元素全是正数),找出该数组中,两个元素相加的最大值,其中被加数的下标大于加数的下标。由加法运算的可逆性,j >i 这个条件可以去掉。
即求出: maxValue = max{arr[j]+arr[i] and j > i}
在数组arr中没有重复的元素情况下,若被加数的下标可以等于加数的下标,则该问题变成了寻找正数数组arr中最大值的元素了。因为 max{arr[i]} + max{arr[i]} 一定比 max{arr[i]} + arr[j] 大,其中arr[j]为数组中的任意某个元素。
而《数据结构与算法分析 Java语言描述》Mark Allen Weiss著 书中第37页 2.28 题中并没有指出数组arr中的元素不通重复。
二,求解思路
有两种思路,一种是对数组中的每个元素,求出该元素与它后面元素的和,然后记录下最大的。
如,对于下标为 i 的元素,for each j belongs to [i+1, arr.length)需要求出 sum=arr[i]+arr[j] 其中,i belongs to [0,arr.length) and j >=i
这算法的时间复杂度为O(N^2)
第二种思路是,先将加数初始化为下标为0的那个元素(arr[i]=arr[0]),然后,j 往后扫描,若遇到比arr[i]大的元素,则下标 i=j。也就是说,i 总是记录比当前arr[i]更大的元素(贪心思想)。
更详细的解释,类似于这篇文章
代码如下:
1 //O(N). find out the max sum of two numbers in a positive array 2 public static int maxSum(int[] arr){ 3 int i = 0; 4 int max = 0;//数组中所有的元素都是正数 5 int addValue; 6 for(int j = 1; j < arr.length; j++){ 7 addValue = arr[i] + arr[j]; 8 if(addValue > max) 9 max = addValue; 10 if(arr[j] - arr[i] > 0) 11 i = j;//记录更大的arr[i] 12 } 13 return max; 14 }
三,运行时间的比较
采用 这篇文章 中提到的随机数生成算法 来随机生成一个数组,然后比较上面两个算法的运行时间。
机器环境如下:
OS:win7 64bit、RAM:6GB、CPU:Pentium(R)Dual-Core E5800@3.2GHz
时间比较如下:
数组大小 maxSum运行时间(O(N)) maxSum2算法2运行时间(O(N^2))
100*100 1 27
200*100 1 68
300*100 2 133
500*100 3 359
此外,按照同样的思路,也可以求解两个数相加的最小值了,哈哈。。。。
求两数相加的最小值,也同样是每次贪心,贪一个较arr[i]更小的值即可,这简直和求解两数相减的最大值一模一样!!!
代码如下:
1 public static int minSum(int[] arr){ 2 int min = Integer.MAX_VALUE; 3 int i = 0; 4 int addValue; 5 for(int j = 1; j < arr.length; j++){ 6 addValue = arr[j] + arr[i]; 7 if(addValue < min) 8 min = addValue; 9 if(arr[j] - arr[i] < 0) 10 i = j; 11 } 12 return min; 13 }
再扩展一下:还可以计算三个数相加的最大值。还是同样的思路,当碰到比上一次运算中的 加数 更大的数时,则把该数替换掉原来加数中最小的那个加数。(三数相加,视为有三个加数。。。^-^)
再扩展一下,是不是感觉到这个问题和 求解最大子序列和 有点联系了? 二者都是直接跳过某些不需要“关注”的元素,并总是“贪心”出 下一个候选元素。
完整代码如下:
1 public class MaxSum { 2 3 //O(N). find out the max sum of two numbers in a positive array 4 public static int maxSum(int[] arr){ 5 int i = 0; 6 int max = 0;//数组中所有的元素都是正数 7 int addValue; 8 for(int j = 1; j < arr.length; j++){ 9 addValue = arr[i] + arr[j]; 10 if(addValue > max) 11 max = addValue; 12 if(arr[j] - arr[i] > 0) 13 i = j;//记录更大的arr[i] 14 } 15 return max; 16 } 17 18 19 public static int maxSum2(int[] arr){ 20 int max = 0; 21 int addValue; 22 for(int i = 0; i < arr.length; i++){ 23 for(int j = i+1; j < arr.length; j++){ 24 addValue = arr[j] + arr[i]; 25 if(addValue > max) 26 max = addValue; 27 } 28 } 29 return max; 30 } 31 32 public static void main(String[] args) { 33 int[] arr = C2_2_8.algorithm3(100*100*10); 34 35 long start = System.currentTimeMillis(); 36 int r = maxSum(arr); 37 long end = System.currentTimeMillis(); 38 System.out.println("maxValue=" + r + " O(N)'s time:" + (end -start)); 39 40 long start2 = System.currentTimeMillis(); 41 int r2 = maxSum2(arr); 42 long end2 = System.currentTimeMillis(); 43 System.out.println("maxValue=" + r2 + " O(N^2)'s time:" + (end2 - start2)); 44 } 45 }