归并算法-JavaScript实现

概念

归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

示意图

在这里插入图片描述

代码实现

递归算法

个人对于递归算法的理解是:

  1. 把数组均分为左右两个子数组(假设子数组是有序数组,如果length%2 !=0,则右边多一个数据 ),然后进行有序数组合并
  2. 左右数组重复上一步的操作
  3. 直到子数组的个数为1,则停止

代码实现:

 /**
   * 归并排序(递归 长度为1500的时候会出现调用栈溢出的问题)
   * @param {*} arr
   * @returns
   */
  sortByMergeRecursion(arr) {
    if (!(arr instanceof Array) || arr.length < 2) {
      return arr;
    }
    const list = arr.concat();
    let length = list.length;
    const merge = (left, right) => {
      let result = [];
      while (left.length > 0 && right.length > 0) {
        if (left[0] < right[0]) {
          result.push(left.shift());
        } else {
          result.push(right.shift());
        }
      }
      result = result.concat(left).concat(right);
      return result;
    };
    const middle = Math.floor(length / 2);
    const left = list.slice(0, middle);
    const right = list.slice(middle);
    return merge(
      this.sortByMergeRecursion(left),
      this.sortByMergeRecursion(right)
    );
  }

非递归算法

当数组长度大于1500时递归算法存在调用栈溢出的问题。因此如果数组长度大于1500时,需要使用非递归算法。
网上有很多讲解的,但是讲得都语焉不详。
这边个人的理解是:

  • 将数组划分为数组长度的2的约数(如果是结果是小数,则向上取整,可以使用Math.ceil()函数),然后对每个子数组进行合并(在第一次分组时,每个子数组只会有2个或者1个数据)
  • 每次循环merge的数据长度为2*i(i表示第几次,i从1开始)
  • 直到2^i >= 数据长度,则说明已经完成了所有有序数组的合并,循环结束

  /**
   * 归并排序(非递归算法)
   * @param {*} arr
   */
  sortByMergePass(arr) {
    if (!(arr instanceof Array) || arr.length < 2) {
      return arr;
    }
    const list = arr.concat();
    let L = list.length;
    let evenLength = 1;
    /**
     *
     * @param {原始列表} list
     * @param {左下标} left
     * @param {中间标} middle
     * @param {右下标} right
     */
    const merge = (list, left, middle, right) => {
      let temp = [];
      let rightStart = middle;
      const leftStart = left;
      const count = right - left + 1;
      const leftEnd = middle - 1;
      // 以下本质是 合并两个有序数组
      while (left <= leftEnd && rightStart <= right) {
        if (list[left] < list[rightStart]) {
          temp.push(list[left]);
          left++;
        } else {
          temp.push(list[rightStart]);
          rightStart++;
        }
      }
      if (left > leftEnd) {
        //左边的已经全部合并完成,,右侧剩下的元素说明都比已经合并的小,则需要把右侧的全部放入到数组中
        while (rightStart <= right) {
          temp.push(list[rightStart]);
          rightStart++;
        }
      }
      if (rightStart > right) {
        while (left <= leftEnd) {
          temp.push(list[left]);
          left++;
        }
      }
      list.splice(leftStart, count, ...temp);
    };
    for (let t = 0; Math.pow(2, t) < L; t++) {
      for (let left = 0; left < L; left += evenLength * 2) {
        let middle = left + evenLength < L ? left + evenLength : left;
        let right = left + evenLength * 2 - 1;
        if (right > L - 1) {
          right = L - 1;
        }
        if (middle <= right) {
          merge(list, left, middle, right);
        }
      }
      // 每次的步幅长度乘以2
      evenLength *= 2;
    }
    return list;
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值