基本思想

  1. 将序列拆成两个或两个以上分别排序,然后再合并成一个
  2. 归并排序使用的 分而治之 是非常经典且常用的算法思想,分而治之思想有以下三个步骤:
    • 分解(Divide):将原问题划分为一些子问题,子问题的形式与原问题相同,只是规模更小;
    • 解决(Conquer):递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解;
    • 合并(Combine):将子问题的解组合成原始问题的解;
  3. 采用分治思想,基于归并操作的一种稳定排序算法(稳定排序:对于关键字相等的元素在排序前后相对顺序保持不变);

图解

复杂度

  1. 时间复杂度:O(NlogN)

    • 递归时长:每一次都切割成原来的一半,即 N、N/2、N/4、......、12^n = Nn = log2^N 👉 O(logN)
    • 每一层处理 N 个元素,一共有 logN 层,则时间复杂度为 O(NlogN)
  2. 空间复杂度:O(N)

    • 每次切割数组要产生两个子数组,这两个子数组的长度和为 N ,则空间复杂度为 O(N)
    • 每次合并两个子数组要创建一个长度为 N 的数组,则空间复杂度为 O(N)
    • 递归调用的深度是 O(logN)
    • 则空间复杂度为 O(N) + O(N) + O(logN) 👉 O(N)

代码实现

/* 归并排序 */
function mergeSort(nums, left, right) {
    // 终止条件
    if (left >= right) return;

    // 划分阶段
    let mid = Math.floor((left + right) / 2); // 计算中点
    mergeSort(nums, left, mid); // 递归左子数组
    mergeSort(nums, mid + 1, right); // 递归右子数组

    // 合并阶段
    merge(nums, left, mid, right);
}

/* 合并左子数组和右子数组 */
function merge(nums, left, mid, right) {
    // 左子数组区间为 [left, mid], 右子数组区间为 [mid+1, right]
    // 创建一个临时数组 tmp ,用于存放合并后的结果
    const tmp = new Array(right - left + 1);
    // 初始化左子数组和右子数组的起始索引
    let i = left,
        j = mid + 1,
        k = 0;

    // 当左右子数组都还有元素时,进行比较并将较小的元素复制到临时数组中
    while (i <= mid && j <= right) {
        if (nums[i] <= nums[j]) {
            tmp[k++] = nums[i++];
        } else {
            tmp[k++] = nums[j++];
        }
    }

    // 将左子数组和右子数组的剩余元素复制到临时数组中
    while (i <= mid) {
        tmp[k++] = nums[i++];
    }
    while (j <= right) {
        tmp[k++] = nums[j++];
    }

    // 将临时数组 tmp 中的元素复制回原数组 nums 的对应区间 [left, right]
    for (k = 0; k < tmp.length; k++) {
        nums[left + k] = tmp[k];
    }
}
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

中午好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 基本思想
  2. 2. 图解
  3. 3. 复杂度
  4. 4. 代码实现