</pre>问题描述:</h1><p></p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">Find the contiguous subarray within an array (containing at least one number) which has the largest sum.</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">For example, given the array <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[−2,1,−3,4,−1,2,1,−5,4]</code>,<br style="" />the contiguous subarray <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[4,−1,2,1]</code> has the largest sum = <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">6</code>.</p><span style="font-family:SimHei; font-size:18px">本例一般有两种解法,第一种被称为Kadane's Algorithm,使用O(n)时间。第二种是利用分支策略(Divide-Conquer),时间复杂度为O(nlogn)。主要描述第二种方法Java实现,思路来自算法导论第四章分治策略。</span><p><span style="font-family:SimHei; font-size:18px">分治主要分为三个步骤:</span></p><p><span style="font-family:SimHei; font-size:18px">分解(Divide):将问题划分为一些子问题,其形式与原问题相同,只是规模更小。</span></p><p><span style="font-family:SimHei; font-size:18px">解决(Conquer):递归求解子问题。如果子问题规模足够小,则停止递归,直接求解。</span></p><p><span style="font-family:SimHei; font-size:18px">合并(Combine):将子问题的解合并成原问题的解。</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">分析本问题,一个数组可以看做由其左右子数组构成,而其最大子序列必定为以下三种情况之一:</span></p><p><span style="font-family:SimHei; font-size:18px">1. 在其左子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">2. 在其右子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">3. 横跨左右子数组</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">因此,根据此分类可到的分治法主程序如下:</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><pre name="code" class="java">private int[] findMaxSubarray(int[] A, int low, int high) {
int[] results = new int[3];//初始化返回数组results,包含最大子数组的low,high和sum
int[] resultsleft, resultsright, resultscross;
int mid;
if (low == high) {
//基本情况(base case):只有单个元素
results[0] = low;
results[1] = high;
results[2] = A[low];
return results;
} else {
//递归情况(recursive case)
mid = (low+high) / 2;
resultsleft = findMaxSubarray(A, low, mid);//递归求解左子序列
resultsright = findMaxSubarray(A, mid+1, high);//递归求解右子序列
resultscross = findMaxCrossSubarray(A, low, mid, high);//求解横跨左右子序列的情况
//比较三种结果,取大者返回
if (resultsleft[2] >= resultsright[2] && resultsleft[2] >= resultscross[2]) return resultsleft;
else if (resultsright[2] >= resultsleft[2] && resultsright[2] >= resultscross[2]) return resultsright;
else return resultscross;
}
}
求解很跨左右子序列情况的方法如下:
</pre>问题描述:</h1><p></p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">Find the contiguous subarray within an array (containing at least one number) which has the largest sum.</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(51,51,51); font-family:'Helvetica Neue',Helvetica,Arial,sans-serif; font-size:14px; line-height:30px">For example, given the array <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[−2,1,−3,4,−1,2,1,−5,4]</code>,<br style="" />the contiguous subarray <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">[4,−1,2,1]</code> has the largest sum = <code style="font-family:Monaco,Menlo,Consolas,'Courier New',monospace; font-size:13px; padding:2px 4px; color:rgb(199,37,78); white-space:nowrap; background-color:rgb(249,242,244)">6</code>.</p><span style="font-family:SimHei; font-size:18px">本例一般有两种解法,第一种被称为Kadane's Algorithm,使用O(n)时间。第二种是利用分支策略(Divide-Conquer),时间复杂度为O(nlogn)。主要描述第二种方法Java实现,思路来自算法导论第四章分治策略。</span><p><span style="font-family:SimHei; font-size:18px">分治主要分为三个步骤:</span></p><p><span style="font-family:SimHei; font-size:18px">分解(Divide):将问题划分为一些子问题,其形式与原问题相同,只是规模更小。</span></p><p><span style="font-family:SimHei; font-size:18px">解决(Conquer):递归求解子问题。如果子问题规模足够小,则停止递归,直接求解。</span></p><p><span style="font-family:SimHei; font-size:18px">合并(Combine):将子问题的解合并成原问题的解。</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">分析本问题,一个数组可以看做由其左右子数组构成,而其最大子序列必定为以下三种情况之一:</span></p><p><span style="font-family:SimHei; font-size:18px">1. 在其左子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">2. 在其右子数组中</span></p><p><span style="font-family:SimHei; font-size:18px">3. 横跨左右子数组</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><p><span style="font-family:SimHei; font-size:18px">因此,根据此分类可到的分治法主程序如下:</span></p><p><span style="font-family:SimHei; font-size:18px"></span></p><pre name="code" class="java">private int[] findMaxSubarray(int[] A, int low, int high) {
int[] results = new int[3];//初始化返回数组results,包含最大子数组的low,high和sum
int[] resultsleft, resultsright, resultscross;
int mid;
if (low == high) {
//基本情况(base case):只有单个元素
results[0] = low;
results[1] = high;
results[2] = A[low];
return results;
} else {
//递归情况(recursive case)
mid = (low+high) / 2;
resultsleft = findMaxSubarray(A, low, mid);//递归求解左子序列
resultsright = findMaxSubarray(A, mid+1, high);//递归求解右子序列
resultscross = findMaxCrossSubarray(A, low, mid, high);//求解横跨左右子序列的情况
//比较三种结果,取大者返回
if (resultsleft[2] >= resultsright[2] && resultsleft[2] >= resultscross[2]) return resultsleft;
else if (resultsright[2] >= resultsleft[2] && resultsright[2] >= resultscross[2]) return resultsright;
else return resultscross;
}
}
private int[] findMaxCrossSubarray(int[] A, int low, int mid, int high) {
int[] results = new int[3];
double leftsum = -1.0 / 0.0;//初始化左部分最大数组和为负无穷
double rightsum = -1.0 / 0.0;//初始化右部分最大数组和为负无穷
double sum = 0;
int maxleft = mid;
int maxright = mid + 1;
for (int i = mid; i >= low; i--) {
//从mid到low计算连续子数组和sum
sum = sum + A[i];
if (sum > leftsum) {
//如果sum比左部分最大数组和大,则更leftsum为sum并记录下标为maxleft
leftsum = sum;
maxleft = i;
}
}
sum = 0;
for (int i = mid+1; i <= high; i++) {
//右子数组处理与左子数组相同
sum = sum + A[i];
if (sum > rightsum) {
rightsum = sum;
maxright = i;
}
}
results[0] = maxleft;
results[1] = maxright;
results[2] = (int)(leftsum + rightsum);
return results;
}
引导程序如下:
public int maxSubArray(int[] A) {
int[] results;
results = findMaxSubarray(A, 0, A.length - 1);
return results[2];
}