分治(divide and conquer)策略的基本思想:
將一個規模爲n的問題分解爲k個規模較小的子問題,這些子問題互相獨立且與原問題相同。遞歸地解這些子問題,然後將各子問題的解合併得到原問題的解。
大致可以通過如下模式來描述:
divide_and_conquer( P ){
if(|P|<= n0) adhoc(P);
divide P into smaller subinstances P1,P2,...,Pk;
for( i = 1 ; i <= k;i++){
yi = divide-and-conquer(Pi);
}
return merge(y1,y2,...yk);
}
其中|P|表示問題P的規模.(具體見王曉東的算法設計與分析或者算法導論中的相關內容)
以下爲分之策略的典型算法:
1.歸併排序(MergeSort)的基本思想是:將待排序元素分成大小大致相同的兩個子集合,分別對兩個子集合進行排序,最終將排好序的子集合合併成所要求的排好序的集合。
大致可以通過如下模式來描述:
void MergeSort(Type arr[],int l,int r){
//MergeSort l...(l+r)/2,對l到(l+r)/2進行歸併排序
MergeSort(arr,l,(l+r)/2);
//MergeSort (l+r)/2+1 ... r,對(l+r)/2+1到r進行歸併排序
MergeSort(arr,(l+r)/2+1,r);
//Merger,合併已經排好序的兩個子序列,使整個序列有序。
merge(arr,l,r);
}
具體代碼如下:最後的合併我使用的事直接插入排序。
template<class T>
void MergeSort(T arr[],int l,int r){
if(l < r){
//MergeSort l...(l+r)/2
MergeSort(arr,l,(l+r)/2);
//MergeSort (l+r)/2+1 ... r
MergeSort(arr,(l+r)/2+1,r);
//Merger,direct insert sort
for(int i = (l+r)/2+1 ; i <= r ; i++ ){
T temp = arr[i];
int j = i-1; //有序序列元素逐漸增多,待排序列元素逐漸減少。
for(j; j >= 0 ; j--){
if(arr[j] > temp ){
arr[j+1] = arr[j];
}else{
break;
}
}
arr[j+1] = temp;
}
}
}
但其實,在合併部分使用直接插入有可能是件極糟糕的事情,在最壞情況下,合併操作(Merge)的時間複雜度爲O(n^2),例如,無序序列{6,7,8,5 ,3,1,4,2}在最後一趟歸併中形成了有序序列{5,6,7,8} 和{1,2,3,4},執行合併操作時,由於算法是直接插入,必然是屬於最壞的情況。對於有n個元素的類似的無序序列,最壞情況下,其時間複雜度爲O(n^2).
如果改變合併操作(Merge)的算法,則可保證合併操作在O(n)內完成,肯定能猜到,它就是合併兩個有序鏈表的算法。
====2013年3月26日更新===========================================
歸併操作過程:https://zh.wikipedia.org/wiki/%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F#.E5.BD.92.E5.B9.B6.E6.93.8D.E4.BD.9C
- 申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
- 設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
- 比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
- 重複步驟3直到某一指針達到序列尾
- 將另一序列剩下的所有元素直接複製到合併序列尾
============================================================
此時,整個歸併排序在最壞情況下所需的計算時間T(n)滿足
T(n) = { O(1) n <= 1
{ 2T(n/2)+O(n) n > 1
解此遞歸方程可得 T(n) = O(nlogn) ,由於排序問題的計算時間下界爲nlogn,故歸併排序算法是一個漸進最優算法。
2.快速排序
快速排序的思想:
算法代碼:
template<class T>
void quickSort(T arr[],int l,int r ){
if(l < r){
//一趟排序
int t = l;
T temp = arr[t];
int i = l,j=r;
while(i < j){
for(;arr[j]>= temp && i<j ;j-- ){
}
if(arr[j]< temp && i<j){ //交換
arr[t] = arr[j];
t = j;
i++;
}
for(;arr[i]<= temp && i<j ; i++){
}
if(arr[i] > temp && i<j){
arr[t] = arr[i];
t = i;
j--;
}
}
arr[i] = temp;
t = i;
//遞歸調用,分解問題
quickSort(arr,l,t-1);
quickSort(arr,t+1,r);
}
}
時間複雜度:O(nlogn)