演算法 歸併排序

2021-08-18 20:31:50 字數 3662 閱讀 4279

前面講過的幾種排序演算法,選擇排序,氣泡排序,插入排序,希爾排序,這些排序可以歸為一類(演算法實現請移步這裡).

因為他們是逐個元素依次比較和交換,其中插入排序對於有序元素有一定的優勢,希爾排序是製造比較優勢.這些排序演算法都是內外迴圈的方式,一直到資料結束.

而歸併排序使用遞迴的方式,將陣列分成無數個小的子陣列,對子陣列進行排序,然後依次將他們歸併起來,從而達到對整個陣列排序的目的.

將兩個有序的陣列歸併起來,從而組成更大的有序陣列.從這個概念衍生出來的遞迴排序思想:歸併排序.要將乙個陣列排序,可以遞迴的將這個陣列的分成兩半,直道他們在最小單位內變成有序時(通常最小單位為1),將陣列合併起來.

總體的流程可以用下面的圖來理解 位址:

先將左半邊陣列進行排序,

再將右半邊元素進行排序,

最後將左邊和右邊的有序陣列進行合併結果

看上面的圖,我們很容易的想到,我們需要三個陣列,左半邊有序陣列,右半邊有序陣列,然後將左右兩半陣列合併到乙個更大的有序陣列中去,但上面的圖只是顯示了乙個抽象的流程.實際中我們需要很多次歸併,所以需要更多的陣列,這顯然會建立大量的陣列,為了避免這種情況,我們可以一次性的分配和原陣列大小相等的另外乙個輔助陣列.在合併的時候,先把原陣列拷貝到輔助陣列,然後利用輔助陣列進行合併. 合併的方法為 

merge(int a, int low, int mid, int height)
通過merge 方法,有效的將a[low]...a[mid]之間的元素和 a[mid+1]...a[height]歸併成乙個有序陣列,並存放在a[low]...a[height]中,其中歸併的過程中,會將原陣列拷貝到輔助陣列中,然後利用輔助陣列,進行排序後,將結果歸併到原陣列:

private static void merge(int a, int low, int mid, int height) 

//merge left and right

for (int k = low; k <= height; k++) else if (j > height) else if (a[i] >= a[j]) else }}

這個方法的簡單描述為:

先將原陣列元素從low...height範圍拷貝到auxiliary(auxiliary是一次性分配的),然後利用下面四個規則將左右兩邊的陣列元素進行重新排序:

如果左半邊元素用盡(i>mid),則使用右半邊的元素.

如果有右半邊的元素用盡(j>height),則使用左半邊的元素.

如果左半邊的當前元素比右半邊的當前元素小,則使用左半邊的當前元素.

如果右半邊的當前元素比坐半邊的當前元素小,則使用右半邊的當前元素.

而且每次選定了元素,不管是左邊(i++)還是右邊(j++),索引都會增加,i,j分別代表兩個陣列中的元素的索引,大家可以仔細想象下,排序過程中,他們的位置依次增加,向後移動查詢元素的模擬場景

為了加深理解理解,看看下面這幅圖:

根據上面我描述的過程,可以看到,排序過程中不斷的在aus[0]...a[4]和 a[5]...a[9]之間比較元素大小,總是將a[i](左半邊陣列)和a[j](右半邊陣列)之間的較小元素重新擺放在a[k]的位置上,直到陣列結束

歸併排序的思路就是,如果能將兩個子陣列排序,就能遞迴的將兩個子陣列排序歸併成乙個更大的有序陣列.

現在給出遞迴排序演算法的乙個實現:

static int nums = new int;

static int auxiliary = new int[nums.length];

public static void main(string args)

public static void toptobottomsort(int nums, int low, int height)

看方法名稱toptobottomsort,叫自頂向下排序,從方法實現上來看,排序時總是先將陣列分成更小的陣列排序,總是將a[low]....a[height]分成 a[low]....a[mid]和 a[mid+1].....a[height],遞迴地將他們進行排序,最後通過merge將他們歸併成最終的結果

歸併的過程為:

上面兩幅圖是歸併排序的軌跡圖,可以看出,

要將a[0]...a[15]排序,先要對a[0]...a[7]排序,

要對a[0]....a[7]排序,要先對a[0]...a[3]排序,

要對a[0]...a[3]排序,要先對a[0]...a[1]排序,

要對a[0]...a[1]排序,要先對a[0]和a[1]進行排序,這個時候,a[0],a[1]已經不能再分隔,作為單個元素來看,他們已經長度為1的有序陣列,可以直接進行合併

a[0]和a[1]歸併後,緊接著就是a[2]和a[3]合併,然後是a[4]...a[7]的排序,同理也是一樣的排序流程,將他們遞迴的分割到只有乙個元素的陣列時,就可以開始歸併排序,和上面的流程是一樣的,依次類推,最後歸併到a[0]...a[15]

這種遞迴排序方式化整為零,遞迴的將排序行為分成更小陣列的排序行為,最後通過很多極小的陣列的排序結果歸併成更大陣列,從而實現對整個陣列的排序.這是"分治思想"在排序演算法上的經典應用.我們將要解決的問題分成許多個小問題的解,然後將小問題的所有解組合起來,最終就構成這個問題的解.

還有另外乙個排序思路,就是與上面的過程完全反過來,叫自底向上排序.

就是先向這個陣列分成很多個極小的陣列(長度為1),然後先兩兩合併,再四四合併,載八八合併.....直道陣列結束.

排序的實現為:

public static void bottomtotopsort(int nums) }}

private static void merge(int a, int low, int mid, int height)

//merge left and right

for (int k = low; k <= height; k++) else if (j > height) else if (a[i] >= a[j]) else }}

用下面這幅圖來加深排序過程的理解:

先分成長度為1的子陣列,兩兩合併,然後將size 加倍,也就是四四合併,然後再加倍,八八合併,直到陣列結束!

前面我們做過實際的 演算法|演算法效能實測(選擇|插入|希爾) ,其中希爾排序是效能最好的,排序 100w隨機資料的時間為193ms.

現在將陣列規模再擴大16倍時(千萬級別),16777216 (1<<24) 個時,排序的時間為: 4663ms,

而歸併排序演算法在這個資料規模上的排序時間為:1425ms,又快了2倍不止.

快速排序的排序效能某些時候比歸併排序還要快,非常值得學習!

演算法實現請移步這裡

歸併演算法 歸併排序

歸併演算法 歸併排序 這周需要用到歸併演算法,於是找了找相關的資料,整理如下 歸併排序 merge sort 是利用 歸併 技術來進行排序。歸併是指將若干個已排序的子檔案合併成乙個有序的檔案。兩路歸併演算法 1 演算法基本思路 設兩個有序的子檔案 相當於輸入堆 放在同一向量中相鄰的位置上 r low...

歸併排序演算法

include stdafx.h include include include 合併兩段已經排好序的列表 void merge int list int mergelist int left int mid int right else if i mid else 將列表list按照seglen分...

歸併排序演算法

這個演算法感覺比插入難理解一些,下面說說我的理解 歸併排序的步驟 1.把長度為n的序列分為兩個長度為n 2的子串行 2.對這兩個子串行分別採用歸併排序 3.將兩個子串行合併成乙個最終的排序序列 通過步驟2可看到 在歸併排序中又用了歸併排序,可見這是乙個遞迴的過程。例如乙個陣列 a 8 下面採用遞迴排...