算法简介
Merge sort,归并排序,是一种非常简单,有效的排序算法.这个算法的名称得于它的原理是将已经排序好的两个序列归并成一个序列.这是一个实现了分治法(devide and conquer)这个算法思想的一个很典型的应用.
分治法简单来说就是将一个原本很复杂的问题分割成为多个简单的子问题(devide),这样我们可以通过解决这些相对简单的小问题(conquer),来完成整个问题的处理.
归并排序在处理三类数据的时候(正序,倒序,以及随机排列),表现都非常不错.这里就只简单比较一下,不深入介绍了.
这里要提一下,归并排序的时间复杂度为O(n*log(n)).
正序数据:肯定是插入排序和冒泡排序最强...归并随着数据量的加大,时间会增加很多.不过结果也很不错.(大量数据的时候快速排序就彻底失败了...)
倒序数据:归并算法结果相比较而言非常理想.插入,冒泡,快速在大数据量的时候完全失败.
随机数据:冒泡与插入的O(n^2)在大数据的劣势已经充分体现出来.这个情况下比较好的排序算法为归并和快速排序.
算法描述
算法可以分为三个步骤(以下步骤通过递归实现):
1.分裂(devide)
将一个完整的列表分割成之前列表长度一半的两个小列表.
2.排序处理(conquer)
比较两个相邻的列表,并且按照指定顺序排列
3.组合(combine)
将上一部处理好的两个列表组合成一个新的列表.
示例
未排序列表 [1, 5, 8, 6, 0, 9, 3, 2]
分割 [1, 5, 8, 6] [0, 9, 3, 2]
分割 [1, 5] [8, 6] [0, 9] [3, 2]
分割 [1] [5] [8] [6] [0] [9] [3] [2]
排序合并 [1, 5] [6, 8] [0, 9] [2, 3]
排序合并 [1, 5, 6, 8] [0, 2, 3, 9]
完成 [0, 1, 2, 3, 5, 6, 8, 9]
比较[0, 9] [2, 3]的顺序是这样的:
首先,比较两个数组的第一个元素,0与2,结果为 0 < 2,于是把0放到合并的数组中,目前结果为 [0]
然后,将2与第一个数组中剩余的9进行比较,结果为2 < 9,将2也放入到数组中,目前结果为 [0, 2]
再将9与第二个数组剩余的3进行比较,结果为 3 < 9, 现在3也可以放入数组了 [0, 2, 3]
最后,因为第二个数组已经空了,就可以直接将9放入结果(因为含有9的这个数组已经排序过了,所以后面不会有比9更小的数字)
结果为 [0, 2, 3, 9]
伪代码
学习一个算法,懂得看伪代码非常重要,基本能看懂伪代码,写出程序就不成问题了.
伪代码其实没有一个特定的格式,熟悉某些编程语言的程序员会写出看起来类似那个语言.不过,最好不要带入太多的编程语言语法,只要表达方法接近,能让大部分程序员很容易读懂就可一了.
PS:程序并不一定完全依照伪代码来写,伪代码在很多时候只是提供了一个思路,让人便于理解.
function mergesort(m)
if length(m) < 1
return m
var list left, right
var int mid = length(m) / 2
left = m[0].....[n/2]
right = m[n/2 + 1]......m[n]
left = mergesort(left)
right = mergesort(right)
return merge(left, right)
end
function merge(left, right)
var list
while length(left) > 0 and length(right) > 0
if left[0] < right [0]
append left[0] to list
remove left[0] from left
else
append right[0] to list
remove right[0] from right
while length(left) > 0
append left[0] to list
remove left[0] from left
while length(right) > 0
append right[0] to list
remove right[0] from right
return list
end
算法实现
这里我考虑使用四种编程语言来实现,分别是c,java,haskell和prolog.大家可以自己比较一下,哪种语言的看起来最舒服,易读,实现起来最方便.
Haskell:
mergeSort :: Ord a => [a] -> [a]
mergeSort [] = []
mergeSort [x] = [x]
mergeSort list =
merge (mergeSort left) (mergeSort right)
where
(left, right) = splitAt (div (length list) 2) list
merge :: Ord a => [a] -> [a] -> [a]
merge [] right = right
merge left [] = left
merge (x:left) (y:right) =
if x <= y then
x:merge left (y:right)
else y:merge(x:left) right
Prolog:
mergesort([], []).
mergesort([X], [X]).
mergesort(List, Result) :-
divide(List, Left, Right),
mergesort(Left, R1),
mergesort(Right, R2),
merge(R1, R2, Result).
divide([], [], []).
divide([X], [Y], []).
divide([X, Y|Rest], [X|R1], [Y|R2]) :-
divide(Rest, R1, R2).
merge(X, [], X).
merge([], Y, Y).
merge([X|R1], [Y|R2], [X|Result]) :-
X =< Y,
merge(R1, [Y|R2], Result).
merge([X|R1], [X|R2], [X|Result]) :-
X > Y,
merge([X|R1], R2, Result).
Java:
public static void mergesort(int[] array) {
mergesort(array, 0, array.length);
}
private static void mergesort(int[] array, int low, int high) {
int mid;
if (high > 1) {
mid = high / 2;//determind size of divided subarrays
mergesort(array, low, mid); // left array
mergesort(array, low + mid, high - mid); //right array
merge(array, low, mid, high - mid); //merge sorted subarrays
}
}
private static void merge(int[] array, int low, int mid, int high) {
int[] temp = new int[mid + high];
int i = 0;
int j = 0;
int k = 0;
// merge elements from subarrays into temp
while (j < mid && k < high)
if (array[low + j] < array[low + mid + k])
temp[i++] = array[low + j++];
else
temp[i++] = array[low + mid + k++];
// add all remaining elements in left and right subarrays into temp
while (j < mid)
temp[i++] = array[low + j++];
while (k < high)
temp[i++] = array[low + mid + k++];
// copy data back from temp
for (int l = 0; l < mid + high; l++)
array[low + l] = temp[l];
}
C: