zoukankan      html  css  js  c++  java
  • 排序算法-(6)归并排序

    归并排序

    归并算法应用到分治策略,简单说就是把一个答问题分解成易于解决的小问题后一个个解决,最后在把小问题的一步步合并成总问题的解。归并排序应用递归来把数组分解成一个个小数组,直到小数组的数位有序,在把有序的小数组两两合并而成有序的大数组。

    最差、最优、平均时间复杂度都为O(nlogn)。辅助空间O(n)。稳定性:稳定。

    算法实现

    function merge(left,right){
        let tmp=[];
        while(left.length&&right.length){//合并左右数组
            if(left[0]<right[0])
                tmp.push(left.shift());
            else
                tmp.push(right.shift());
        }
        return tmp.concat(left,right);
    }
    function mergeSort(a){
        if(a.length==1)//终止条件
            return a;
        let mid=a.length>>1,
            left=a.slice(0,mid),
            right=a.slice(mid);
        return merge(mergeSort(left),mergeSort(right))
    }

    这段合并排序的代码相当简单直观,但是mergeSort()函数会导致很频繁的自调用。一个长度为n的数组最终会调用mergeSort() 2*n-1次,这意味着如果需要排序的数组长度很大会在某些栈小的浏览器上发生栈溢出错误。

    这里插个话题,关于递归调用时浏览器的栈大小限制,可以用代码去测试

    var cnt = 0;
    try {
      (function() {
        cnt++;
        arguments.callee();
      })();
    } catch(e) {
      console.log(e.message, cnt);
    }
    
    
    // chrome: Maximum call stack size exceeded 35992
    // firefox: too much recursion 11953

    遇到栈溢出错误并不一定要修改整个算法,只是表明递归不是最好的实现方式。这个合并排序算法同样可以迭代实现,比如(摘抄自《高性能JavaScript》):

    function merge(left, right) {
        let result = [];
        while (left.length && right.length) {
            if (left[0] < right[0])
                result.push(left.shift());
            else
                result.push(right.shift());
        }
        return result.concat(left, right);
    }
    function mergeSort(a) {
        if (a.length === 1)
            return a;
        let work = [],len=a.length;
        for (let i = 0; i < len; i++)
            work.push([a[i]]);
        work.push([]); // 如果数组长度为奇数
        let last=0;
        for (let lim = len; lim > 1; lim = (lim + 1)>>1) {
            for (let j = 0, k = 0; k < lim; j++, k += 2) {
                work[j] = merge(work[k], work[k + 1]);
                last=j;
            }
            work[last+1] = []; // 如果数组长度为奇数
        }
        return work[0];
    }

    这个版本的mergeSort()函数功能与前例相同却没有使用递归。尽管迭代版本的合并排序算法比递归实现要慢一些,但它并不会像递归版本那样受调用栈限制的影响。把递归算法改用迭代实现是实现栈溢出错误的方法之一。

  • 相关阅读:
    全排列问题(内测第0届第1题)
    提取字符串中的数字(C语言)
    尾递归=递归+迭代?
    Android各版本代号、版本号、API/NDK级别、发布时间及市场份额
    C语言中文件打开模式(r/w/a/r+/w+/a+/rb/wb/ab/rb+/wb+/ab+)浅析
    sizeof既是关键字,又是运算符(操作符),但不是函数!
    探寻main函数的“标准”写法,以及获取main函数的参数、返回值
    Scala比较器:Ordered与Ordering
    【Python实战】Pandas:让你像写SQL一样做数据分析(一)
    【Python实战】Scrapy豌豆荚应用市场爬虫
  • 原文地址:https://www.cnblogs.com/wuguanglin/p/mergeSort.html
Copyright © 2011-2022 走看看