问题:
数组中的一个数字减去它右边子数组中的一个数字可以得到一个差值,求所有可能的差值中的最大值,例如,数组{1,4,17,3,2,9}中,最大差值为17-2=15。
方法一:
暴力法。双重循环遍历数组,寻找任意两个元素之间的差值,并取最大值。时间复杂度:O(n)
方法一代码如下:
package com.haobi;
/*
* 方法一:暴力法。
*/
public class Test18 {
public static void main(String[] args) {
int[] a = {1,4,17,3,2,9};
System.out.println(getMax(a));
}
public static int getMax(int[] a) {
if(a == null) {
return Integer.MAX_VALUE;
}
int len = a.length;
if(len<=1) {
return Integer.MIN_VALUE;
}
//定义变量存储最大值
int max = Integer.MIN_VALUE;
//双重循环
for(int i=0;i<len-1;i++) {
for(int j=i+1;j<len;j++) {
if(a[i] - a[j] > max) {
max = a[i] - a[j];
}
}
}
return max;
}
}
程序输出结果如下:
15
方法二:
分治法/递归法。把数组分为两个子数组,那么最大的差值只能有3种可能:
(1)最大差值对应的被减数和减数都在左子数组中,设为leftMax。
(2)最大差值对应的被减数和减数都在右子数组中,设为rightMax。
(3)被减数是左子数组的最大值,减数是右子数组的最小值,设为minMax。
因此,max(leftMax,rightMax,minMax)就是这个数组的最大差值。
此外,为了保证数据的传递性和可变性,采用了AtomicInteger原子操作类。
时间复杂度:O(n)
方法二代码如下:
package com.haobi;
import java.util.concurrent.atomic.AtomicInteger;
/*
* 方法二:分治法。
*/
public class Test19 {
public static void main(String[] args) {
int[] a = {1,4,17,3,2,9};
System.out.println(getMax(a));
}
public static int getMax(int[] a) {
if(a == null) {
return Integer.MAX_VALUE;
}
int len = a.length;
if(len<=1) {
return Integer.MIN_VALUE;
}
//定义变量存储最大/小值
AtomicInteger max = new AtomicInteger(0);
AtomicInteger min = new AtomicInteger(0);
return getMaxDiff(a,0,len-1,max,min);
}
public static int getMaxDiff(int[] a, int begin, int end, AtomicInteger max, AtomicInteger min) {
if(begin==end) {
max.set(a[begin]);
min.set(a[begin]);
return Integer.MIN_VALUE;
}
int middle = begin + (end-begin)/2;
//第一种可能
//数组前半部分的最大值与最小值
AtomicInteger lMax = new AtomicInteger(0);
AtomicInteger lMin = new AtomicInteger(0);
//数组前半部分的最大差值(第一种可能)
int leftMax = getMaxDiff(a,begin,middle,lMax,lMin);
//第二种可能
//数组后半部分的最大值与最小值
AtomicInteger rMax = new AtomicInteger(0);
AtomicInteger rMin = new AtomicInteger(0);
//数组后半部分的最大差值(第二种可能)
int rightMax = getMaxDiff(a,middle+1,end,rMax,rMin);
//第三种可能
int minMax = lMax.get() - rMin.get();
//求数组的最大值与最小值
if(lMax.get() > rMax.get()) {
max.set(lMax.get());
}else {
max.set(rMax.get());
}
if(lMin.get() < rMin.get()) {
min.set(lMin.get());
}else {
min.set(rMin.get());
}
//求最大差值
int allMax = (leftMax > rightMax) ? leftMax : rightMax;
allMax = (allMax > minMax) ? allMax : minMax;
return allMax;
}
}
程序输出结果如下:
15
方法三:
动态规划法。
给定数组a,申请额外的数组diff和max,其中diff[i]是以数组中第i个数字为减数的所有数对之差的最大值(前i+1个数组成的子数组中最大的差值),max[i]为前i+1个数的最大值。假设已经求得了diff[i],diff[i+1]的值有两种可能性:①等于diff[i];②等于max[i]-a[i];通过分析可以得到动态方程:diff[i+1]=max(diff[i],max[i]-a[i+1]),max[i+1]=max(max[i],a[i+1])。数组最大的差值为diff[n-1](n为数组的长度)。
时间复杂度:O(n),空间复杂度:O(n)。
方法三代码如下:
package com.haobi;
/*
* 方法三:动态规划。
*/
public class Test20 {
public static void main(String[] args) {
int[] a = {1,4,17,3,2,9};
System.out.println(getMax(a));
}
public static int getMax(int[] a) {
if(a == null) {
return Integer.MAX_VALUE;
}
int len = a.length;
if(len<=1) {
return Integer.MIN_VALUE;
}
int[] diff = new int[len];
int[] max = new int[len];
diff[0] = 0;
max[0] = a[0];
for(int i=1;i<len;i++) {
diff[i] = max(diff[i-1], max[i-1]-a[i]);
max[i] = max(max[i-1], a[i]);
}
return diff[len-1];
}
public static int max(int m, int n) {
return (m>n)? m : n;
}
}
程序输出结果如下:
15
方法四:
动态规划法(改进)。
由于在方法三中,在求解diff[i+1]时,只用到了diff[i]与max[i],因此将方法三中申请的两个数组变为变量,从而降低算法的空间复杂度。
时间复杂度:O(n),空间复杂度:O(1)。
方法四代码如下:
package com.haobi;
/*
* 方法三:动态规划。
*/
public class Test21 {
public static void main(String[] args) {
int[] a = {1,4,17,3,2,9};
System.out.println(getMax(a));
}
public static int getMax(int[] a) {
if(a == null) {
return Integer.MAX_VALUE;
}
int len = a.length;
if(len<=1) {
return Integer.MIN_VALUE;
}
int diff = 0;
int max = a[0];
for(int i=1;i<len;i++) {
diff = max(diff, max-a[i]);
max = max(max, a[i]);
}
return diff;
}
public static int max(int m, int n) {
return (m>n)? m : n;
}
}
程序输出结果如下:
15