分而治之
分而治之(D&C)是一種重要的算法設計範例。在分治策略中,我們遞歸地求解一個問題,在每層遞歸中應用如下三步驟:
- 分解(Divide):將問題劃分爲一些子問題,子問題的形式與原問題一樣,只是規模更小
- 解決(Conquer):遞歸地求解出子問題。如果子問題規模足夠小,則停止遞歸,直接求解
- 合併(Combine):將子問題的解組合成原問題的解
分治的核心是合併(Combine/Merge)
例子(Examples):
- Maximum Contiguous Subarray(最大子數組)
- Counting Inversions(逆序計數)
- Integer Multiplication(整數乘法)
- Ploynomial Multiplication(多項式乘法)
- QuickSort and Partition(快速排序與劃分)
- Deterministic and Randomized Selection(確定性與隨機化選擇)
Maximum Contiguous Subarray(最大子數組)
正式定義:
- 輸入:一實數數組A[1...n]
- 子數組A[i...j]的值是:
找到i <= j,使V(i,j)最大化。
暴力求解
計算每一對i <= j的V(i,j)的值,返回最大值。c++代碼實現:
/***************************************************************
暴力求解(Brute Force):
計算每一對i <= j的V(i,j)的值,返回最大值。
複雜度:O(n^3)
****************************************************************/
double MCS_BF(vector<double> &A)
{
int sz = A.size();
if(sz == 0){
cout << "輸入數組爲空" << endl;
return -1;
}
double vmax = A[0];
for(int i= 0; i < sz; ++i){
for(int j = i; j < sz; ++j){
double sum = 0;
for(int x = i; x <= j; ++x){
sum += A[x];
}
if(sum > vmax)
vmax = sum;
}
}
return vmax;
}
數據再利用
不需要從頭計算每一個V(i,j), V(i,j) = V(i,j-1) + A[j]。c++代碼實現:
/****************************************************************
數據再利用(Data_Reuse) :
不需要從頭計算每一個V(i,j), V(i,j) = V(i,j-1) + A[j]
複雜度:O(n^2)
*****************************************************************/
double MCS_DR(vector<double> &A)
{
int sz = A.size();
if(sz == 0){
cout << "輸入數組爲空" << endl;
return -1;
}
double vmax = A[0];
for(int i= 0; i < sz; ++i){
double sum = 0;
for(int j = i; j < sz; ++j){
sum += A[j];
if(sum > vmax)
vmax = sum;
}
}
return vmax;
}
分而治之
設 ,
數組A[low...high]的最大子數組定是以下三種之一:
- :位於A[low...m];
- :位於A[m+1...high];
- SM:跨越中點
.
c++代碼實現:
/****************************************************************
分而治之(Divide-and-Conquer)
複雜度:O(nlogn)
*****************************************************************/
double FindMaxCrossingSubarr(vector<double> &A, int low, int mid, int high)
{
double SM1 = A[mid];
double sum = A[mid];
for(int i = mid-1; i >= low; --i){
sum += A[i];
if(sum > SM1)
SM1 = sum;
}
double SM2 = A[mid + 1];
sum = A[mid + 1];
for(int j = mid + 2; j <= high; ++j){
sum += A[j];
if(sum > SM2)
SM2 = sum;
}
return SM1+SM2;
}
double FindMaxSubarr(vector<double> &A, int low, int high)
{
if(low == high)
return A[low];
int m = floor((low + high) / 2);
double S1 = FindMaxSubarr(A,low,m);
double S2 = FindMaxSubarr(A,m+1,high);
double SM = FindMaxCrossingSubarr(A,low,m,high);
return max({S1,S2,SM});
}
double MCS_DC(vector<double> &A)
{
if(A.empty()){
cout << "輸入數組爲空" << endl;
return -1;
}
int low = 0;
int high = A.size() - 1;
return FindMaxSubarr(A,low,high);
}
運行效果:
用O(n)時間複雜度解決:Kadane算法