最大子数组问题
1.背景
《算法导论》中是通过股票的购买与出售,经过问题转换,将前一天的当天的股票差价重新表示出来,即转为了一个最大子数组的问题,相当于就是寻找一个数组的和最大的非空连续子数组
如 13, -3, -25, 20, -3, -16, -23, 18, 20, -7, 12, -5, -22, 15, -4, 7
找到这连续的16个数里面的连续和最大的子数组,具体详情可以网上搜索。
我们利用分治法来解决这个问题。
2.Python实现
#Find_max_subarray
#python3
#Yanglin Tu
def find_max_crossing_subarray(arr,low,mid,high):
left_sum = -float('inf')
sum = 0
for i in range(mid,low-1,-1):
sum = sum + arr[i]
if sum > left_sum:
left_sum = sum
max_left = i
right_sum = -float('inf')
sum = 0
for j in range(mid+1,high+1):
sum = sum + arr[j]
if sum > right_sum:
right_sum = sum
max_right = j
return (max_left,max_right,left_sum+right_sum)
def find_maximum_subarray(arr,low,high):
if low == high:
return(low,high,arr[low])
else:
mid = (low + high)//2
left_low,left_high,left_sum = find_maximum_subarray(arr,low,mid)
right_low,right_high,right_sum = find_maximum_subarray(arr,mid+1,high)
cross_low,cross_high,cross_sum = find_max_crossing_subarray(arr,low,mid,high)
if left_sum > right_sum and left_sum > cross_sum:
return(left_low,left_high,left_sum)
elif right_sum > left_sum and right_sum > cross_sum:
return(right_low,right_high,right_sum)
else:
return (cross_low,cross_high,cross_sum)
def main():
list_a = [13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7]
length = len(list_a)
low_a,high_a,sum_a = find_maximum_subarray(list_a,0,length-1)
print(low_a,high_a,sum_a)
if __name__ == '__main__':
main()
3.C++实现
//Find_max_subarray
//C++
//Yanglin Tu
#include <iostream>
using namespace std;
struct arr_result
{
int max_left;
int max_right;
int max_sum;
};
arr_result find_max_crossing_subarray(int arr[],int low,int mid,int high);
arr_result find_maximum_subarray(int arr[],int low,int high);
int main(){
int list_a[] = {13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
int length = sizeof(list_a)/sizeof(int);
arr_result result = find_maximum_subarray(list_a,0,length-1);
cout<<result.max_left<<'\t'<<result.max_right<<'\t'<<result.max_sum<<endl;
return 0;
}
arr_result find_max_crossing_subarray(int arr[],int low,int mid,int high){
int left_sum = -0x3f3f3f3f;
int right_sum = -0x3f3f3f3f;
int sum = 0;
int i,j,max_left,max_right;
for(i = mid;i>=low;i--){
sum = sum + arr[i];
if (sum > left_sum){
left_sum = sum;
max_left = i;
}
}
sum = 0;
for(j=mid+1;j<=high;j++){
sum = sum + arr[j];
if (sum > right_sum){
right_sum = sum;
max_right = j;
}
}
arr_result result{max_left,max_right,left_sum+right_sum};
return result;
}
arr_result find_maximum_subarray(int arr[],int low,int high){
arr_result result;
if(low == high)
return result = {low,high,arr[low]};
else{
int mid = (low + high)/2;
arr_result left_result = find_maximum_subarray(arr,low,mid);
arr_result right_result = find_maximum_subarray(arr,mid+1,high);
arr_result cross_result = find_max_crossing_subarray(arr,low,mid,high);
if (left_result.max_sum > right_result.max_sum && left_result.max_sum > cross_result.max_sum)
return left_result;
else if (right_result.max_sum > left_result.max_sum && right_result.max_sum > cross_result.max_sum)
return right_result;
else
return cross_result;
}
}
4.性能分析
此算法的时间复杂度为O(nlogn)。
5.采用一个新的思想来解决,此时间复杂度为O(n)
使用如下思想为最大子数组问题设计一个非递归的、线性时间的算法。从数组的左边界开始,从左至右处理,记录到目前为止已经处理过的最大子数组。若已知A[1..j]的最大子数组,基于如下性质将解扩展为A[1..j+1]的最大子数组:A[1..j+1]的最大子数组要么是A[1..j]的最大子数组,要么是某个子数组A[i..j+1] (1≤i≤j+1)。在已知A[1..j]的最大子数组的情况下,可以在线性时间内找出形如A[i..j+1]的最大子数组。
下面用Python实现
#Find_max_subarray_1
#python3
#Yanglin Tu
def find_maximum_subarray(arr):
length = len(arr)
sum = arr[0]
bounary = arr[0]
for i in range(length-1):
if bounary > 0 :
bounary += arr[i+1]
else:
bounary = arr[i+1]
if bounary > sum:
sum = bounary
return sum
def main():
list_a = [13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7]
sum_a = find_maximum_subarray(list_a)
print(sum_a)
if __name__ == '__main__':
main()