《算法导论》学习笔记——寻找最大子数组
寻找最大子数组
1.问题描述
给定一个数组A,找到数组A的一个子数组使得该子数组内所有元素的和最大。当然,如果A中的元素恒为正或恒为负,那么A的最大子数组即为A自身或A中最大的负数,这是很简单的情况。但如果A中的元素有正有负,问题就比较复杂。如下面的数组A,其最大子数组应该为{18,20,-7,12}。
如果采用暴力求解法,那么需要我们至少需要检查n(n-1)/2,即组合数Cn2,所以暴力求解法的算法复杂度为O(n2)(n2:n*n,即n的平方)。所以我们采用一种较好的方法来求解最大子数组——分治法。
2.使用分治策略求解最大子数组
假设我们要寻找数组A[low...high]的最大子数组,分治法的策略即为我们要将数组尽量等分为两个规模一样的数组,因此。可以找到数组的中间位置mid,然后求解两个子数组A[low...mid]和A[mid+1...high]。如下图所示,A[low...high]的任何连续子数组A[i...j]必然属于下列情况:
- 完全位于子数组A[low...mid]中,因此low <= i <= j <= mid;
- 完全位于子数组A[mid+1...high]中,因此mid < i <= j <= high;
- 跨域了中点mid,因此low <= i <= mid < j <= high.
因此,A[low...high]的最大子数组必然属于上述三种情况之一,对于前两种情况,我们只需采用分治策略递归地求解最大子数组即可;我们要思考的只是对于跨域中点的子数组的求解以及在这三种情况中选取最大的子数组。这也是容易求得的,根据上面的分析,任何跨越了中点的子数组都是由两个子数组A[low...mid]和A[mid+1...high]组成,且满足条件low <= i <= mid < j <= high。因此,我们只需要找出形如A[low...mid]和A[mid+1...high]的最大子数组,然后将其合并即可。
最后对算法复杂度进行评价:采用分治策略求解最大子数组的算法复杂度为O(nlogn)。
3.代码实现(C,Java,Python)
C
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef struct {
int left;
int right;
int sum;
}Subarray;
Subarray subarray;
Subarray findMaxCrossSubarray(int* array, int low, int mid, int high) {
int i, left_sum , right_sum;
left_sum = right_sum = INT_MIN;
Subarray subarray_cross;
subarray_cross.sum = 0;
for(i = mid; i >= low; i--) {
subarray_cross.sum += array[i];
if(subarray_cross.sum > left_sum) {
left_sum = subarray_cross.sum;
subarray_cross.left = i;
}
}
subarray_cross.sum = 0;
for(i = mid + 1; i <= high; i++) {
subarray_cross.sum += array[i];
if(subarray_cross.sum > right_sum) {
right_sum = subarray_cross.sum;
subarray_cross.right = i;
}
}
subarray_cross.sum = left_sum + right_sum;
return subarray_cross;
}
Subarray findMaxSubarray(int* array, int low, int high) {
int mid;
Subarray subarray_left;
Subarray subarray_right;
Subarray subarray_cross;
if(low == high) {
subarray.left = low;
subarray.right = high;
subarray.sum = array[low];
return subarray;
}
else {
mid = (low + high) / 2;
subarray_left = findMaxSubarray(array, low, mid);
subarray_right = findMaxSubarray(array, mid + 1, high);
subarray_cross = findMaxCrossSubarray(array, low, mid, high);
if(subarray_left.sum > subarray_right.sum && subarray_left.sum > subarray_cross.sum)
return subarray_left;
else if(subarray_right.sum > subarray_left.sum && subarray_right.sum > subarray_cross.sum)
return subarray_right;
else
return subarray_cross;
}
}
int main() {
int *array, len, i;
printf("Enter the length of the array: ");
scanf("%d", &len);
array = (int* )malloc(len * sizeof(int));
for(i = 0; i < len; i++)
scanf("%d", &array[i]);
subarray = findMaxSubarray(array, 0, len - 1);
printf("The index of the maximum subarray form %d to %d, and the sum is %d.\n", \
subarray.left + 1, subarray.right + 1, subarray.sum);
printf("The maximum subarry is consist of: ");
for(i = subarray.left; i <= subarray.right; i++)
printf("%d ", array[i]);
return 0;
}
Java
import java.util.*;
public class FindMaxSubarray {
public static void main(String[] args){
Scanner in = new Scanner(System.in);
ArrayList<Integer> array = new ArrayList<Integer>();
HashMap<String, Integer> subarray = new HashMap<String, Integer>();
System.out.print("Enter the length of array: ");
int length = in.nextInt();
System.out.print("Enter the elements of array: ");
for(int i = 0; i < length; i++)
array.add(in.nextInt());
in.close();
Find find = new Find(array);
subarray = find.findSubArray(find.getLow(), find.getHigh());
System.out.print("The value of the maximum subarray is " + subarray.get("sum") +
" , from the index " + (subarray.get("low") + 1) + " to the index " +
(subarray.get("high") + 1) + ".");
System.out.print("The elements of the maximum subarray are: ");
for(int i = subarray.get("low"); i <= subarray.get("high"); i++)
System.out.print(array.get(i) + " ");
System.out.print(".");
}
}
class Find{
public Find(ArrayList<Integer> array) {
this.array = array;
}
public HashMap<String, Integer> findSubArray(int low, int high){
if (low == high){
subarray.put("low", low);
subarray.put("high", high);
subarray.put("sum", array.get(low));
return subarray;
}
else {
int mid = (low + high) / 2;
subarrayLeft = findSubArray(low, mid);
subarrayRight = findSubArray(mid + 1, high);
subarrayCross = findMaxCrossSubarray(low, mid, high);
if (subarrayLeft.get("sum") >= subarrayRight.get("sum") &&
subarrayLeft.get("sum") >= subarrayCross.get("sum")){
return subarrayLeft;
}
else if (subarrayRight.get("sum") >= subarrayLeft.get("sum") &&
subarrayRight.get("sum") >= subarrayCross.get("sum")){
return subarrayRight;
}
else {
return subarrayCross;
}
}
}
public HashMap<String, Integer> findMaxCrossSubarray(int low, int mid, int high){
int sum = 0;
int leftSum = Integer.MIN_VALUE;
int maxLeft = 0;
for (int i = mid; i >= low; i--){
sum += array.get(i);
if (sum > leftSum){
leftSum = sum;
maxLeft = i;
}
}
int rightSum = Integer.MIN_VALUE;
int maxRight = 0;
sum = 0;
for (int i = mid + 1; i <= high; i++){
sum += array.get(i);
if (sum > rightSum){
rightSum = sum;
maxRight = i;
}
}
subarray.put("low", maxLeft);
subarray.put("high", maxRight);
subarray.put("sum", leftSum + rightSum);
return subarray;
}
public int getLow(){
return 0;
}
public int getHigh(){
return array.size() - 1;
}
private ArrayList<Integer> array;
private HashMap<String, Integer> subarray = new HashMap<String, Integer>();
private HashMap<String, Integer> subarrayLeft = new HashMap<String, Integer>();
private HashMap<String, Integer> subarrayRight = new HashMap<String, Integer>();
private HashMap<String, Integer> subarrayCross = new HashMap<String, Integer>();
}
Python
findMaxSubarray.py
import sys
def findMaxSubarray(array, low, high):
if low == high:
subarray = {"low": low, "high": high, "sum": array[low]}
return subarray
else:
mid = (low + high) / 2
subarrayLeft = findMaxSubarray(array, low, mid)
subarrayRight = findMaxSubarray(array, mid + 1, high)
subarrayCross = findMaxCrossSubarray(array, low, mid, high)
if subarrayLeft["sum"] > subarrayRight["sum"] and subarrayLeft["sum"] > subarrayCross["sum"]:
return subarrayLeft
elif subarrayRight["sum"] > subarrayLeft["sum"] and subarrayRight["sum"] > subarrayCross["sum"]:
return subarrayRight
else:
return subarrayCross
def findMaxCrossSubarray(array, low, mid, high):
sum = 0;
leftSum = sys.maxint * -1
maxLeft = 0
for i in range(mid, low, -1):
sum += array[i];
if sum > leftSum :
leftSum = sum
maxLeft = i
sum = 0;
RightSum = sys.maxint * -1
maxRight = 0
for i in range(mid + 1, high):
sum += array[i];
if sum > RightSum :
RightSum = sum
maxRight = i
crossSubArray = {"low": maxLeft, "high": maxRight, "sum": leftSum + RightSum}
return crossSubArray
test.py
import findMaxSubarray
array = [13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7]
length = len(array) - 1
subarray = []
maxSubarray = findMaxSubarray.findMaxSubarray(array, 0, length)
print "The maximum of subarray is " + str(maxSubarray["sum"]) + \
" from index " + str(maxSubarray["low"] + 1) + \
" to index " + str(maxSubarray["high"] + 1) + "."
for i in range(maxSubarray["low"], maxSubarray["high"] + 1):
subarray.append(array[i])
print "The elements of the maximum subarry are " + str(subarray) + "."