演算法時空-最大子陣列問題
阿新 • • 發佈:2019-01-08
文章目錄
最大子陣列問題
給定陣列,求算其中的最大子陣列,要求返回最後的最大子陣列的左下標 l 和右下標 r ,以及最大和 s ;
- 思路1:暴力解決,O(n^2)複雜度,這裡具體就不用程式碼實現;
- 思路2:使用分治法,通過假定最大子陣列處於左邊,中間和右邊,來最終得出準確的結果;
- 思路3:使用動態規劃演算法,最常用的高效演算法;
1. 分治法
int main()
{
int a[] = {2,4,-7,5,2,-1,2,-4,3};
int l, r, sum;
l = 0, r = 8;
sum = findMaxSubarray(a, l, r);
printf("左邊元素是a[%d]=%d, 右邊元素是a[%d]=%d, 最大和sum=%d\n", l, a[l], r, a[r], sum);
return 0;
}
/*
求算最大子陣列,傳入陣列a指標,儲存a最左邊下標的l地址,最右邊下標的r地址;
*/
int findMaxSubarray(int *a, int &l, int&r) {
if(l == r) return a[l];
int mid = l + (r - l) / 2;
int left_min = l, left_max = mid; // 因為l和r都得中間存值,所以每次遞迴時候,需要用新的變數去儲存;
int left_num = findMaxSubarray(a, left_min, left_max); // 最大子陣列僅存在左半邊時;
int right_min = mid+1, right_max = r; //同上;
int right_num = findMaxSubarray(a, right_min, right_max);
int cross_num = findMaxFromMid (a, l, r); // 計算,當最大子陣列貫穿中間mid元素時;
if(left_num >= right_num && left_num >= cross_num) {
l = left_min;
r = left_max;
return left_num;
}
else if(right_num >= left_num && right_num >= cross_num) {
l = right_min;
r = right_max;
return right_num;
}
else return cross_num;
}
/* 對於陣列a[],傳入最左邊下標l,最右邊下標r,計算返回子陣列中最大和;
* 但是,這裡預設是該子陣列計算中,必須包含mid元素!
*/
int findMaxFromMid(int *a, int& l, int& r) {
if(l == r) return a[l];
int mid = l + (r - l) / 2;
int sum = a[mid], left_sum = a[mid], left_index = mid; //sum不從0開始取值,使得left_sum數值不必取負無窮;
for(int i=mid-1; i>=l; i--) {
sum += a[i];
if(sum > left_sum) {
left_sum = sum; //找到最大和;
left_index = i; //找到此時的下界;
}
}
sum = a[mid];
int right_sum = a[mid], right_index = mid;
for(int i=mid+1; i<=r; i++) {
sum += a[i];
if(sum > right_sum) {
right_sum = sum;
right_index = i;
}
}
l = left_index; //將求算得到的下界賦給全域性變數;下同;
r = right_index;
return left_sum + right_sum - a[mid];; //a[mid]計算了兩次,故返回最大和時候要減去一個;
}
-
結果展示:
輸入陣列:[2,4,-7,5,2,-1,2,-4,3];
輸出最大子陣列:
-
演算法總結:
時間複雜度T(n) = 2T(n/2)+O(n);根據主定理,最終執行時間複雜度為O(nlog(n));
2. 動態規劃
int main() {
int a[] = {2,4,-7,5,2,-1,2,-4,3};
int l = 0, r = 8;
int sum = findMaxSubarray(a, l, r);
printf("左邊元素是a[%d]=%d, 右邊元素是a[%d]=%d, 最大和sum=%d\n", l, a[l], r, a[r], sum);
}
/*
求算最大子陣列,傳入陣列a指標,儲存a最左邊下標的l地址,最右邊下標的r地址;
*/
int findMaxSubarray(int *a, int &l, int &r) {
if(l == r) return a[l];
int cur = a[l], total = a[l]; //cur 為當前最大和值,total為全域性最大和值
int left_index = l, right_index = l; //動態記錄左右下標
for(int i=l+1; i<=r; i++) { // O(n)級別
// 其實下面這一句程式碼本來應該為 if(cur + a[i] < a[i]),當前最大和值指的是**必須**包含當前a[i]元素
if(cur < 0) { //那麼如果之前的cur拖累的當前a[i],那麼久丟掉a[i-1]元素
cur = a[i];
left_index = i; // 記錄左下標
}
else cur += a[i]; // 否則加上當前元素
if(cur > total) {
total = cur;
right_index = i; // 記錄右下標
}
}
l = left_index;
r = right_index;
return total;
}
- 演算法總結:
此演算法的時間複雜度為O(n)級別,通過找區域性最大,然後求全域性最大的思路,最終得出結論;