归并排序核心思想:
如果要排序一个数组,先把数组从中间分成2部,然后对这2部分分别排序,再将排序好的数组进行合并。
归并排序使用的是分治思想,即将一个大问题分解成小的子问题来解决,小的子问题解决了,大问题也就解决了。
这里用递归来实现归并排序
说下写递归代码的技巧:分析出递归公式,找到终止条件,最后将递归公式翻译成代码。
递归公式:
merge_sort(l…r) = merge_sort(l…mid) + merge_sort(mid…r)
终止条件:
l>=r
解释下这个递归公式:
merge_sort(l…r)表示给下标l到r的数组排序,将这个排序问题转成了2个子问题merge_sort(l…mid)和merge_sort(mid…r),下标mid等于下标l到r的中间位置,当下标l到mid和下标mid到r这2个子数组排序好后,将2个子数组合并,这样下标l到r的数组也排序好了。
参考代码
PHP版
public function mergeSort($arr) {
$len = count($arr);
// 递归终止条件
if ($len <= 1) {
return $arr;
}
// 取数组中间位置
$mid = intval($len / 2);
// 拆分数组0-mid这部分给左边left
$left = array_slice($arr, 0, $mid);
// 拆分数组mid-末尾这部分给右边right
$right = array_slice($arr, $mid);
//分治递归
$left = $this->mergeSort($left);
$right = $this->mergeSort($right);
// 合并两个数组
$arr = $this->merge($left, $right);
return $arr;
}
public function merge($arr_a, $arr_b) {
$res = [];
$point_a = $point_b = 0;
$len_a = count($arr_a) - 1;
$len_b = count($arr_b) - 1;
//移动数据
while($point_a <= $len_a && $point_b <= $len_b) {
if ($arr_a[$point_a] <= $arr_b[$point_b]) {
$res[] = $arr_a[$point_a++];
} else {
$res[] = $arr_b[$point_b++];
}
}
//判断哪个数组有剩余数据
if ($point_b <= $len_b) {
while ($point_b <= $len_b) {
$res[] = $arr_b[$point_b++];
}
} else {
while ($point_a <= $len_a) {
$res[] = $arr_a[$point_a++];
}
}
return $res;
}
性能分析
-
是稳定排序算法吗?
结合上面的代码,可以发现,归并排序稳不稳定关键看merge()函数。
合并过程中,如果arr_a和arr_b中有相同元素,先把arr_a中的元素放进res数组,这样保证了值相同的元素,合并前后先后顺序不变,所以是稳定排序算法 -
时间复杂度
最好,最好,平均情况,时间复杂度都是O(nlogn) -
空间复杂度
在任意时刻,CPU中只会有一个函数在执行,所以只有一个临时内存空间在使用,临时内存空间最大也不会超过n个数据,所以空间复杂度是O(n)