歸併排序
實現原理
所謂歸併排序,指的是如果要排序乙個陣列,我們先把陣列從中間分成前後兩部分,然後對前後兩部分分別排序,再將排好序的兩部分合併在一起,這樣整個陣列就都有序了。
歸併排序使用了分治思想,分治,顧名思義,就是分而治之,將乙個大問題分解成小的子問題來解決。說到這裡,可能你就能聯想起我們之前講到的乙個程式設計技巧 —— 遞迴,沒錯,歸併排序就是通過遞迴來實現的。這個遞迴的公式是每次都將傳入的待排序陣列一分為二,直到不能分割,然後將排序後序列合併,最終返回排序後的陣列。
原理圖如下所示:
由於涉及到遞迴,所以歸併排序從理解上要比前面三個排序要困難一些,還是建議通過這個動態圖幫助理解:在介面頂部選擇歸併排序,然後在左下角點選執行即可。
示例**
歸併排序的 php 實現**如下:
<?php
function merge_sort($nums)
merge_sort_c($nums, 0, count($nums) - 1);
return $nums;
}function merge_sort_c(&$nums, $p, $r)
$q = floor(($p + $r) / 2);
merge_sort_c($nums, $p, $q);
merge_sort_c($nums, $q + 1, $r);
merge($nums, ['start' => $p, 'end' => $q], ['start' => $q + 1, 'end' => $r]);
}function merge(&$nums, $nums_p, $nums_q)
else
}if ($i <= $nums_p['end'])
}if ($j <= $nums_q['end'])
}for ($x = 0; $x < $k; $x++)
}$nums = [4, 5, 6, 3, 2, 1];
$nums = merge_sort($nums);
print_r($nums);
效能分析
最後我們來總結下,歸併排序不涉及相等元素位置交換,是穩定的排序演算法,時間複雜度是 o(nlogn),要優於氣泡排序和插入排序的 o(n^2),但是歸併排序需要額外的空間存放排序資料,不是原地排序,最多需要和待排序陣列同樣大小的空間,所以空間複雜度是 o(n)。
歸併排序的時間複雜度計算過程:
歸併的思路時將乙個複雜的問題 a 遞迴拆解為子問題 b 和 c,再將子問題計算結果合併,最終得到問題的答案,這裡我們將歸併排序總的時間複雜度設為 t(n),則 t(n) = 2*t(n/2) + n,其中 t(n/2) 是遞迴拆解的第一步對應子問題的時間複雜度,n 則是合併函式的時間複雜度(乙個迴圈遍歷),依次類推,我們可以推導 t(n) 的計算邏輯如下:
t(n) = 2*t(n/2) + n
= 2*(2*t(n/4) + n/2) + n = 4*t(n/4) + 2*n
= 4(2*t(n/8) + n/4) + 2*n = 8*t(n/8) + 3*n
= ...
= 2^k*t(n/2^k) + k*n
遞迴到最後,t(n/2^k)≈t(1),也就是 n/2^k = 1,計算歸併排序的時間複雜度,就演變成了計算 k 的值,2^k = n,所以 k=log2 n,我們把 k 的值帶入上述 t(n) 的推導公式,得到:
t(n) = n*t(1) + n*log2n = n(c + log2n)
把常量和低階忽略,所以 t(n) = nlogn。 排序演算法 歸併排序
歸併排序是建立在歸併操作上的一種有效的排序演算法。該演算法是採用分治法 divide and conquer 的乙個非常典型的應用。首先考慮下如何將將二個有序數列合併。這個非常簡單,只要從比較二個數列的第乙個數,誰小就先取誰,取了後就在對應數列中刪除這個數。然後再進行比較,如果有數列為空,那直接將另...
排序演算法 歸併排序
include include define status int define max 20 typedef struct elemtype typedef struct sqlist void inital sqlist l 初始化 bool lt int i,int j void merge ...
排序演算法 歸併排序
歸併排序的思想其實完全是分治法的思想的體現,它完全遵循分治法的模式。這裡有必要再重提下分治法的思想 將原有的問題分解為幾個規模較小的但類似於原問題的子問題,遞迴的求解這些子問題,然後再合併這些子問題的解來求得原問題的解。現在來看看歸併排序的操作 1 將等待排序的含有 n 個元素的序列分解成各具有 n...