這次主要談談大名鼎鼎的快速排序。
快速排序其實也是分治演算法的一種應用,它將陣列分為兩個子陣列,然後分別將陣列排序,當子陣列有序時整個陣列也就有序了。在歸併排序中,陣列被均分為兩部分,而在快速排序中切分(partition)取決於陣列中的內容。快速排序遞迴的將子陣列a[lo...hi]排序,先用partition()方法將a[j]放到合適的位置,然後再用遞迴呼叫將其他元素排序。而快速排序的關鍵在於切分,這個過程使得陣列滿足下列條件:
1.對於某個j,a[j]已經排定;
2.a[lo]到a[j-1]中的所有元素都不大於a[j];
3.a[j+1]到a[hi]中的所有元素都不小於a[j]。
要實現這個方法一般策略是先隨意的取a[lo]作為切分元素,即那個將會被排定的元素,然後我們從陣列的左端開始掃瞄直到找到乙個大於等於它的元素,再從陣列的右端開始找到乙個小於等於它的元素。這兩個元素顯然是沒有排定的,因此交換他們的位置,如此繼續,就可以保證左指標i的左側元素都不大於切分元素,右指標j右側的元素都不小於切分元素。當兩個指標相遇時,我們只需要將切分元素a[lo]和左子陣列最右側的元素(a[j])交換然後返回j即可。
時間複雜度的討論:將長度為n的無重複陣列排序,快速排序平均需要2nlnn次比較(以及1/6的交換)。快速排序最多需要約n^2/2次比較,但隨即打亂能夠預防這種情況。(可以使用c++的algorithm庫中shuffle函式來進行打亂序列。具體的用法可以google)
下面給出快速排序的各個方法具體實現**:
//切分陣列
templateint partition(t array, int lo, int hi)
}while (v < array[--j])
}if (i >= j)
std::swap(array[i], array[j]);
}std::swap(array[lo], array[j]);
return j;
}
//快速排序的遞迴主程式
templatevoid qsort_(t array, int lo, int hi)
//對外提供的介面
templatevoid quick_sort(t array, int length)
快速排序對於小陣列的優勢其實並不明顯,可以考慮再陣列元素小於20時改用插入排序。下面再考慮一種改進方法,名為「三向切分的快速排序」,它從左向右遍歷陣列一次,維護乙個指標lt使得a[lo...lt-1]中的所有元素都小於v,乙個指標gt使得a[gt+1...hi]中的所有元素都大於v,乙個指標i使得a[lt...i-1]中的元素都等於v。
下面給出「三向切分快速排序的遞迴例程」:
//三向切分的快速排序
templatevoid qsort_3way_(t array, int lo, int hi) else if (array[i] > v) else
}qsort_3way_(array, lo, lt - 1);
qsort_3way_(array, gt + 1, hi);
}
時間複雜度分析:
對於大小為n的陣列,三向切分的快速排序需要(2ln2)nh次比較,其中h是主鍵值出現的頻率定義的夏農資訊量。
其實對於標準的快速排序,隨著陣列規模的增大其執行時間會趨於平均執行時間。對於包含大量重複元素的陣列,三向切分快速排序將時間複雜度從線性對數級降到線性級別。快速排序已經在計算機界得到了廣泛使用。說到這裡,很多人以為排序演算法到這裡就結束了,其實不然,排序其實不一定要建立在比較的基礎上,後面還會討論這些巧妙的排序演算法。
演算法三之堆排序
一 堆 heap 定義 1 n個關鍵字序列kl,k2,kn稱為 heap 當且僅當該序列滿足如下性質 簡稱為堆性質 k i k 2i 且k i k 2i 1 1 i n 2 當然,這是小根堆,大根堆則換成 號。2 k i 相當於二叉樹的非葉子結點,k 2i 則是左子節點,k 2i 1 是右子節點 若...
演算法三之堆排序
1 n個關鍵字序列kl,k2,kn稱為 heap 當且僅當該序列滿足如下性質 簡稱為堆性質 k i k 2i 且k i k 2i 1 1 i n 2 當然,這是小根堆,大根堆則換成 號。2 k i 相當於二叉樹的非葉子結點,k 2i 則是左子節點,k 2i 1 是右子節點 若將此序列所儲存的向量r ...
排序演算法之 直接選擇排序(三)
直接選擇排序與直接插入排序類似,都將資料分為有序區與無序 區,不同點 直接插入排序是將無序區第乙個元素直接插入到有序區形成 更大的有序區,直接選擇排序是從無序區選乙個最小的元素直接放到有序區最後 include include include define n 10 using namespace ...