1. 程式人生 > >演算法時空-最大子陣列問題

演算法時空-最大子陣列問題

文章目錄

最大子陣列問題

給定陣列,求算其中的最大子陣列,要求返回最後的最大子陣列的左下標 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)級別,通過找區域性最大,然後求全域性最大的思路,最終得出結論;