二分归并排序算法_常用排序算法之归并排序

    点击上方蓝色 “铁匠学编程” 关注我,让我们一起学习!

    前面给大家分享了冒泡排序、插入排序和选择排序,他们的时间复杂度都是O(n2),比较高,适合小规模的数据排序。今天我们就分享一种时间复杂度为O(nlogn)的算法,它就是--归并排序

归并排序原理

    它的原理比较简单,就是将一个数组从中间分为两部分然后分别进行排序,最后将排好序的两部分合并到一起,这样数组就有序了。

原理如下图:

f8d972e9e766d5201130b4f159d9ba6c.png

归并排序用到了分治思想,字面就是分而治之,将一个大问题分成若干个小问题单独进行处理,后面我们再单独讲下。

从上图也可以看出,归并用到了递归,归并一般都是通过递归来实现的。归并是一种解决问题的思想,递归是一种编程技巧。

代码示例

Go示例:

package mainimport "fmt"func MergeSort(arr []int) {  len := len(arr)  if len <= 1 {    return  }  mergeSort(arr, 0, len-1)}func mergeSort(arr []int, start, end int) {  if start >= end {    return  }  middle := (start + end) / 2  mergeSort(arr, start, middle)  mergeSort(arr, middle+1, end)  merge(arr, start, middle, end)}func merge(arr []int, start, middle, end int) {  tempArr := make([]int, end-start+1)  i := start  j := middle + 1  k := 0  for ; i <= middle && j <= end; k++ {    if arr[i] <= arr[j] {      tempArr[k] = arr[i]      i++    } else {      tempArr[k] = arr[j]      j++    }  }  for ; i <= middle; i++ {    tempArr[k] = arr[i]    k++  }  for ; j <= end; j++ {    tempArr[k] = arr[j]    k++  }  copy(arr[start:end+1], tempArr)}func main() {  arr := []int{8, 3, 4, 5, 9, 2, 1}  MergeSort(arr)  fmt.Println(arr)}

PHP示例:

function merge_sort($nums){    if (count($nums) <= 1) {        return  $nums;    }    merge_sort_c($nums, 0, count($nums) - 1);    return $nums;}function merge_sort_c(&$nums, $p, $r){    if ($p >= $r) {        return;    }    $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){    $temp = [];    $i = $nums_p['start'];    $j = $nums_q['start'];    $k = 0;    while ($i <= $nums_p['end'] && $j <= $nums_q['end']) {        if ($nums[$i] <= $nums[$j]) {            $temp[$k++] = $nums[$i++];        } else {            $temp[$k++] = $nums[$j++];        }    }    if ($i <= $nums_p['end']) {        for (; $i <= $nums_p['end']; $i++) {            $temp[$k++] = $nums[$i];        }    }    if ($j <= $nums_q['end']) {        for (; $j <= $nums_q['end']; $j++) {            $temp[$k++] = $nums[$j];        }    }    for ($x = 0; $x < $k; $x++) {        $nums[$nums_p['start'] + $x] = $temp[$x];    }}$nums = [4, 5, 6, 3, 2, 1];$nums = merge_sort($nums);print_r($nums);

JS示例

const mergeArr = (left, right) => {    let temp = []    let leftIndex = 0    let rightIndex = 0    while (left.length > leftIndex && right.length > rightIndex) {        if (left[leftIndex] <= right[rightIndex]) {            temp.push(left[leftIndex])            leftIndex++        } else {            temp.push(right[rightIndex])            rightIndex++        }    }    return temp.concat(left.slice(leftIndex)).concat(right.slice(rightIndex))}const mergeSort = (arr) => {    if (arr.length <= 1) return arr    const middle = Math.floor(arr.length / 2)    const left = arr.slice(0, middle)    const right = arr.slice(middle)    return mergeArr(mergeSort(left), mergeSort(right))}const testArr = []let i = 0while (i < 100) {    testArr.push(Math.floor(Math.random() * 1000))    i++}const res = mergeSort(testArr)console.log(res)

性能分析

最后我们看下归并排序的性能和稳定性:

  1. 时间复杂度:是O(nlogn),要优于冒泡和插入排序

  2. 空间复杂度:需要额外的空间存放排序的数据,不是原地排序

  3. 算法稳定性:不涉及相等元素位置交换,是稳定的排序算法

归并排序时间复杂度计算方式:

    归并的思想将一个复杂的问题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/2k)≈T(1),也就是 n/2k = 1,计算归并排序的时间复杂度,就演变成了计算 k 的值,2k = n,所以 k=log2n,我们把 k 的值带入上述 T(n) 的推导公式,得到:

T(n) = n*T(1) + n*log2n = n(C + log2n) //其中2为log的下标

    写在最后:最近工作拿到一个需求不大,但涉及部门所有核心业务花费了很多时间学习历史代码和请教同事,昨天停更,深深的一种负罪感!? 记得小浩算法大佬就分享过该怎么持续输出,不一定是每天都写,时间多的时候可以多输出一些,看来最近要好好学习,刚开始分享学习了不少大神的思路和方式,感谢!

    最近将有“大事”发生,密谋已久希望成功!关注不会让你失望哦~

都到这了,加个关注吧!

213748f734c1755854a064ef999a17fc.png

加油!不仅自己~还有你~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值