歸併(merge)排序法是將兩個(或兩個以上)有序表合併成乙個新的有序表,即把待排序序列分為若干個有序的子串行,再把有序的子串行合併為整體有序序列。
假設待排序的序列:43
7928
6先說思路,歸併排序的中心思想是將兩個已經排序好的序列,合併成乙個排序的序列。
上面的序列可以分成:43
79和2
86這兩個序列,然後對這兩個序列分別排序:結果為:
設定為序列a,與序列b,34
7926
8將上面的兩個序列 合併成乙個排序好的序列:
合併的具體思路是:
設定兩個位置指示器,分別指向序列a與序列b開始的位置:紅色為指示器指向位置:34
7926
8比較兩個指示器所指向的元素的值,將較小的插入到乙個新的陣列內,例如序列c,同時將對應的指示器向後移動一位:
結果為:34
7926
8形成的序列c:已經被插入乙個元素了,剛才較小的 元素2.
2然後 再次 比較序列a與序列b中指示器所指向的元素:將小的放入到序列c中,移動相應指標,結果為:34
7926
823以此類推,迭代執行,直到序列a或者序列b中某個指示器已經移到到陣列末端。例如:
多次比較後,序列b已經將指示器移出到序列末端(最後乙個元素之後)了。34
7926
8234
678然後將沒有用完的序列,這裡面是序列a中其餘的元素全部插入到序列c的後邊即可,就剩下乙個9 了,將其插入到序列c後即可:
序列c結果:23
4678
9這樣就實現了將兩個有序序列合併成乙個有序序列的操作,
下面先看這個合併的php**:
/** * 將兩個有序陣列合併成乙個有序陣列
* @param $arra,
* @param $arrb,
* @reutrn array合併好的陣列 */
function mergearray($arra, $arrb
) else
}//判斷 陣列a內的元素是否都用完了,沒有的話將其全部插入到c陣列內:
while($a_i
< $a_len
)
//判斷 陣列b內的元素是否都用完了,沒有的話將其全部插入到c陣列內:
while($b_i
< $b_len
)
return
$arrc;}
經過上面的分析和程式的實現,我們不難發現,合併已排序的序列的時間應該是線性的,就是說,最多會發生n-1次比較,其中n是所有元素之和。
通過上面的描述,我們實現了將兩個排序好的陣列進行和並的過程。
此時,大家可能會有疑問,這個和歸併排序整個序列有什麼關係?或者你是如何能夠得到最開始的兩個排序好的子串行的呢?
下面,我們就來描述以下什麼是歸併排序,然後再看,上面的合併與歸併排序的關係是如何的:
大家不妨去想,當我們需要排序如下的陣列時,我們是否可以先將陣列的前半部分與陣列的後半部分分別進行歸併排序,然後將排序的結果合併起來呢?
例如:待排序的陣列:43
7928
6先分成2部分:43
7928
6將前半部分 與 後半部分 分別看成乙個序列,再次進行歸併(就是拆分,排序,合併)操作
就會變成:前:4
379後:
286同樣 再對每個自序列進行 歸併排序,再次(拆分,排序,合併)。
當拆分的子串行內只存在乙個元素(長度為1)時,那麼這個序列就不必再拆分了,就是乙個排序好的陣列了。然後將這個序列,與其他的序列再合併到一起即可,最終就將所有的都合併好了,成為乙個完整的排序好的陣列。
程式實現:
通過上面的描述 大家應該想到,可以使用遞迴程式來實現這個程式設計吧:
想要實現這個程式,可能需要解決如下問題:
怎麼將陣列做拆分:
設定兩個指示器,乙個指向陣列開始假定為$left,乙個指向陣列最後乙個元素$right:43
7928
6然 後判斷 $left
是否小於$right,如果小於,說明這個序列內元素個數大於乙個,就將其拆分成兩個陣列,拆分的方式是生成乙個中間的指示器$center,值
為$left + $right /2 整除。結果為:3,然後將$left 到$center 分成一組,$center+1到$right分成一組:43
7928
6接下來,遞迴的 利用$left, $center, $center+1, $right分別做為 兩個序列的 左右指示器,進行操作。知道陣列內有乙個元素$left==$right .然後按照上面的合併陣列即可:
/*** mergesort 歸併排序
* 是開始遞迴函式的乙個驅動函式
* @param &$arr array 待排序的陣列
*/function mergesort(&$arr
) /*
** 實際實現歸併排序的程式
* @param &$arr array 需要排序的陣列
* @param $left int 子串行的左下標值
* @param $right int 子串行的右下標值
*/function msort(&$arr, $left, $right) }
/*** 將兩個有序陣列合併成乙個有序陣列
* @param &$arr, 待排序的所有元素
* @param $left, 排序子陣列a的開始下標
* @param $center, 排序子陣列a與排序子陣列b的中間下標,也就是陣列a的結束下標
* @param $right, 排序子陣列b的結束下標(開始為$center+1)
*/function mergearray(&$arr, $left, $center, $right
) else
}//判斷 陣列a內的元素是否都用完了,沒有的話將其全部插入到c陣列內:
while($a_i
<= $center
)
//判斷 陣列b內的元素是否都用完了,沒有的話將其全部插入到c陣列內:
while($b_i
<= $right)
//將$arrc內排序好的部分,寫入到$arr內:
for($i=0, $len=count($temp); $i
<$len; $i++) }
//do some test:
$arr = array(4, 7, 6, 3, 9, 5, 8);
mergesort(
$arr
);print_r($arr);
注意上面的**帶排序的陣列都使用的是 引用傳遞,為了節約空間。
而且,其中的合併陣列的方式也為了節約空間做了相對的修改,把所有的操作都放到了$arr上完成,引用傳遞節約資源。
好了 上面的**就完成了歸併排序,歸併排序的時間複雜度為o(n*logn) 效率還是相當客觀的。
再說,歸併排序演算法,中心思想是
將乙個複雜問題分解成相似的小問題,再把小問題分解成更小的問題,直到分解到可以馬上求解為止,然後將分解得到的結果再合併起來的一種方法。這個思想用個
成語形容叫化整為零。 放到電腦科學中有個專業屬於叫分治策略(分治發)。分就是大問題變小問題,治就是小結果合併成大結果。
分治策略是很多搞笑演算法的基礎,我們在討論快速排序時,也會用到分治策略的。
最後簡單的說一下這個演算法,雖然這個演算法在時間複雜度上達到了o(nlogn)。但是還是會有乙個小問題,就是在合併兩個陣列時,如果陣列的總元素
個數為
n,那麼我們需要再開闢乙個同樣大小的空間來儲存合併時的資料(就是mergearray中的$temp陣列),而且還需要將資料有$temp拷貝
會$arr,因此會浪費一些資源。因此在實際的排序中還是 相對的較少使用。
歸併排序 詳細解釋版
歸併 merge 排序法是將兩個 或兩個以上 有序表合併成乙個新的有序表,即把待排序序列分為若干個有序的子串行,再把有序的子串行合併為整體有序序列。假設待排序的序列 4 3 79 2 8 6先說思路,歸併排序的中心思想是將兩個已經排序好的序列,合併成乙個排序的序列。上面的序列可以分成 4 3 79 ...
歸併排序詳細解析
我們先來乙個兩個有序的陣列a和b進行排序的 兩個有序的陣列進行排序只需每次選擇兩個陣列中最小的那個數放進c中就ok了,之後如果那個陣列還有剩餘就將其直接接在c後面。時間效率還是很快的達到了o n python原始碼 def memeryarray a,b,c i 0 j 0 k 0 n len a ...
5 歸併排序(詳細)
目錄 sort類在選擇排序中的約定中 歸併排序的思想是將陣列分成兩部分,分別進行排序,然後歸併起來。歸併方法將陣列中兩個已經排序的部分歸併成乙個。算是乙個輔助方法把,下面不管哪種歸併排序都會用到 public abstract class mergesort comparable extends s...