常用排序演算法 合併排序和快速排序

2022-09-02 03:54:11 字數 1853 閱讀 5429

分治(divide and conquer)是一場常見的演算法策略。分治策略的基本思想就是對於乙個問題規模為n的問題,將其劃分為規模足夠小的k個子問題,子問題由於規模足夠小可以直接求解,最後將規模足夠小的k的問題的解合併得出原問題的解。

分治策略的問題的求解過程的一般套路就是:

判斷問題規模,足夠小進入步驟2,否者進入步驟3

直接對問題進行求解,返回問題的解。

將問題拆分成k個小規模的問題,進入步驟2。並將解合併,得到原問題的解。

由於分治策略的步驟描述自帶遞迴屬性,因此分治策略的演算法實現常採用遞迴來實現。遞迴的演算法通常會含有三部分,第一部分是個基準情況(base case),第二部分是問題拆分,並呼叫自身,第三部分是合併並返回結果。

分治策略中主要有兩個關鍵過程:

分治策略的主要時間開銷發生在這兩個過程,通常分治策略有兩類

拆分容易,合併困難

拆分困難,合併簡單

下文中的合併排序快速排序分別屬於上面的第1和第2類。

在常見排序演算法中,合併排序和快速排序是典型的分治演算法,其時間複雜度的平均效能均為nlgn。合併排序的最壞時間複雜度也能保持nlgn,而快速排序的最壞時間複雜度卻為n*n合併排序是穩定的,而快速排序不是穩定的。另外合併排序是拆分容易,合併困難,而快速排序則是拆分困難,合併容易。

合併排序又稱歸併排序,主要的思想是:將待排序列拆分至數個足夠小的子串行,然後將相鄰子串行合併為乙個有序子串行,重複合併相鄰有序子串行直到整個序列有序。

快速排序的主要思想則是,將待排序列拆分為左右兩個子串行a,b,使得子串行a的元素都小於子串行b,然後對子序列a,b繼續進行這種拆分,直到待拆分的序列足夠小,最後整個序列變成有序。

由於合併排序和快速排序每次元素的移動都不只移動了乙個位置,因此每次元素的比較都不只消除了乙個逆序對,因此對於插入排序這種每次比較只移動乙個位置的演算法,時間複雜度會得到改善。

int partition(int* a, int l, int h) 

a[j] = pivot;

return j;

}void quicksort(int* a, int l, int h)

void merge(int* a, int l, int m, int h) 

void mergesort(int* a, int l, int h)

由於遞迴會導致函式呼叫棧的暴增,會引入額外的時間開銷,例如現場保護和恢復之類的。通遞迴版本的演算法都可以改寫為迭代版本的演算法。遞迴演算法的優點是實現簡單直觀,易於理解,缺點是如果有時候呼叫深度過深,會帶來棧記憶體溢位和額外的執行時間開銷。迭代演算法的有點是不會有額外的函式呼叫棧的增長, 缺點是難於理解且難於實現。

合併排序的迭代版本的實現相對來講是比較簡單直觀的,大致思路就是:第1輪,待排序列s中相鄰的長度為1的子串行進行合併,第2輪,序列s中相鄰的長度為2的子串行進行合併,第i輪,待排序列s中相鄰的長度為2的i-1次方的子串行進行合併。直到待合併的子串行數為1。

void merge(int* a, int l, int m, int h) 

//非遞迴版本

void mergeiteation(int* a, int n)

offset *= 2;

}}

排序方法,合併排序 快速排序

function mergesort myarray var middle math.floor myarray.length 2 left myarray.slice 0,middle right myarray.slice middle params merge mergesort left m...

排序演算法 合併排序

這個排序比起冒泡,選擇什麼的就快很多了,時間複雜度是n logn,但是實現起來也很複雜。原理是 第一步,合併1 2 3 4 5 6.相鄰兩個排序 第二步,合併1 2 3 4 5 6 7 8 9.相鄰四個排序 實現起來最主要的是合併的函式,廢話不多說,看 class mergersorter priv...

排序演算法 合併排序

說明 之前所介紹的排序法都是在同乙個陣列中的排序,考慮今日有兩筆或兩筆以上的資料,它可能是不同陣列中的資料,或是不同檔案中的資料,如何為它們進行排序?解法 可以使用合併排序法,合併排序法基本是將兩筆已排序的資料合併並進行排序,如果所讀入的資料尚未排序,可以先利用其它的排序方式來處理這兩筆資料,然後再...