1. 程式人生 > >最大子陣列問題的分治求解演算法

最大子陣列問題的分治求解演算法

最大子陣列問題,就是尋找一個數組內連續元素之和最大的子陣列,如陣列[1,-2,3,4,5,-6],則最大子陣列為[3,4,5]。當然,只有當陣列內有負數值時才有意義,如果全部為正數,則最大子陣列就是其本身。

假如有陣列arr下標從lowhigh,以中點索引mid作為區分,索引low~mid為左部分,mid+1~high為右部分,則最大子陣列可能存在於左部分、右部分以及橫跨左右部分。

假如最大子陣列在左部分或右部分,其仍是一個最大子陣列問題,只是規模更小,因此可遞迴地求解arr[low~mid]arr[mid+1~high]的最大子陣列。
假如陣列橫跨左右兩個部分,則從中間索引mid開始,向low

high延伸,先找到arr[mid~low]中的最大子陣列(注意,從mid開始向兩邊找),然後找到arr[mid+1~high]的最大子陣列。最後將兩邊的最大子陣列進行合併,即求出橫跨左右部分的最大子陣列。

現在我們可以分別求得三個部分的最大子陣列:左部的最大子陣列、右部的最大子陣列、橫跨左右部的最大子陣列。然後對其元素和進行比較。擁有最大元素和的最大子陣列即所求。

下面是演算法實現:

#include<iostream>
using namespace std;

//用於將求得的最大子陣列資訊進行打包
struct subarray_info{
  int left;  //左界
int right; //右界 int sum; //最大子陣列和 }; //尋找橫跨左右兩部分的最大子陣列,並打包成subarray_info結構 void find_max_crossing_subarray(int* arr,int low,int mid,int high,subarray_info* psub){ int sum=0; int left_sum=-10000; //左部分最大子陣列和 int right_sum=-10000; //右部分最大子陣列和 for(int i=mid;i>=low;i--){ //從mid開始,到low,求得左部的最大子陣列 sum
+=arr[i]; //和大於left_sum,則求得更大的子陣列 if(left_sum<sum){ left_sum=sum; psub->left=i; //更新最大子陣列左界資訊,左界即此最大子陣列在arr中的左部索引 } } sum=0; for(int i=mid+1;i<=high;i++){ //從mid+1開始,到high,求得右部的最大子陣列 sum+=arr[i]; if(right_sum<sum){ //同上 right_sum=sum; psub->right=i; //更新最大子陣列右界資訊 } } psub->sum=left_sum+right_sum; //最大子陣列的和即左右部分最大子陣列元素和的和 } //尋找arr下標從low到high的最大子陣列,並將其資訊打包成subarray_info結構 void find_maximum_subarray(int* arr,int low,int high,subarray_info* psub){ if(low==high){ //所求的陣列只有一個元素值 psub->left=low; psub->right=high; psub->sum=arr[low]; } else{ //所求的陣列有多個元素 int mid=(low+high)/2; //獲取中間索引 //分別用不同的subarray_info來儲存不同部分的最大子陣列 subarray_info* p1=new subarray_info(); subarray_info* p2=new subarray_info(); subarray_info* p3=new subarray_info(); find_maximum_subarray(arr,low,mid,p1); //左部最大子陣列 find_maximum_subarray(arr,mid+1,high,p2); //右部最大子陣列 find_max_crossing_subarray(arr,low,mid,high,p3); //橫跨左右部分的最大子陣列 int sum1=p1->sum; //左部最大子陣列的和 int sum2=p2->sum; //右部 int sum3=p3->sum; //橫跨左右部 if(sum1>=sum2 && sum1>=sum3){ //左部的最大,更新psub psub->left=p1->left; psub->right=p1->right; psub->sum=p1->sum; } else if(sum2>=sum1 && sum2>=sum3){ //右部的最大 psub->left=p2->left; psub->right=p2->right; psub->sum=p2->sum; } else{ //橫跨兩部分的最大 psub->left=p3->left; psub->right=p3->right; psub->sum=p3->sum; } } } int main(){ int arr[16]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7}; subarray_info* psub=new subarray_info(); find_maximum_subarray(arr,0,15,psub); cout<<"左界:"<<psub->left<<endl; cout<<"右界:"<<psub->right<<endl; cout<<"最大子陣列元素和:"<<psub->sum<<endl; return 0; } RESULT: 左界:7 右界:10 最大子陣列元素和:43