演算法原理與分析之分治法
一.目錄
1.演算法基本原理
2.經典問題
二.分治法基本原理
分而治之,先將原問題的規模下降,分解為子問題,此所謂“分”,然後解決子問題,此為“治”。
分治法的基本思想是將一個規模為n的原問題分解為k個規模較小的子問題,這些子問題互相獨立且與原問題相同。遞迴地解這些子問題,然後將子問題的解合併為原問題的解。
分治法的虛擬碼:
v divide_and_conquer(proplem p){//n為問題規模
if(|p|<n0)//n0為一閾值
solve(p);
else{
divide p into smaller subproblem P1,P2,...Pk;
for(i=1;i<=k;i++){
yi=divide_and_conquer(Pi);
return merge(y1,y2,...,yk);
}
}
}
可以看到,分治法與遞迴是一對孿生兄弟,有分治法的地方就有遞迴的身影。
使用分治法時,要將原問題進行規模分解,分解為獨立的子問題。一般來說,將原問題分解為兩個大小相同的子問題可以得到將好的效率。
對分治法的複雜度分析是通過對遞迴的複雜度進行分析。遞迴複雜度分析方法有三種,即主定理,遞迴樹,代入法。
三.經典問題
3.1.合併排序
3.2.快速排序
3.3.線性時間選擇
3.1合併排序
合併排序是利用分治法對n個元素進行排序的方法。其基本思想,先將原元素集合分解為規模大小基本相同的兩個子集合,然後依次遞迴地對子問題進行排序。最後將排好序的子集合合併為所要求的排好序的集合。
虛擬碼:
void merge_sort(Type a[],int left,int right){
if(left<right){ //至少兩個元素
int i = (left+right)/2;
merge_sort(a,left,i);
merge_sort(a,i+1,right);
merge(a,b,left,i,right);//合併到陣列b
copy(a,b,left,right);//複製回陣列a
}
}
void merge(Type origin[],Type destint[],int left,int middle,int right){
int i=left; int k=left; int j=middle+1;
while(i<middle && j<right){
if(origin[i]<origin[j]) destin[k++] = origin[i++];
else destin[k++] = origin[j++];
}
//處理剩餘元素
if(i<middle){
while(i<middle) destin[k++] = origin[i++];
else
while(j<right) destin[k++] = origin[j++];
}
}
分治法的複雜度分析:
分治法的遞迴語句複雜度為T(n/2);
合併(merge) n個元素的複雜度為O(n);
複製(copy) n個元素的複雜度為O(n);
O(1) n<=1;
T(n) ={
T(n/2) + O(n); n>=2;
可以得出分治法的平均複雜度為O(nlogn);