数据结构与算法——排序2(归并排序)

104 阅读3分钟
  • 冒泡排序、插入排序、选择排序时间复杂度都是 O(n^2),适合小规模数据的排序。
  • 归并排序和快速排序的时间复杂度为 O(nlogn)。适合大规模的数据排序。
  • 归并排序和快速排序都用到了分治思想(将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之)

一、🐶 归并排序的原理

  • 归并排序使用的就是分治思想,就是分而治之,将一个大问题分解成小的子问题来解决。小的子问题解决了,大问题也就解决了。
  • 分治思想跟递归思想很像。分治算法一般都是用递归来实现的。分治是一种解决问题的处理思想,递归是一种编程技巧。
  1. 分解:将数组递归地分解成两个较小的子数组,直到子数组的等于1。此时,每个子数组都被视为“已排序”。
  2. 递归进行排序并合并:递归地对子数组进行排序,并将已排序的子数组合并成一个大的有序数组,直到合并为1个完整的数组。

二、🐶 归并排序的递推公式和结束条件

  // 结束条件
  if (arr.length = 1) {  
    return arr;  
  }
  
  // 递推公式
  // merge_sort(l…r) 表示,给下标从 l 到 r 之间的数组排序。我们将这个排序问题转化为2个子问题,merge_sort左边和merge_sort右边,等到merge_sort左边和merge_sort右边排好序之后我们再把2个有序数组合并在一起。
  merge_sort(l…r) = merge(merge_sort(l…mid), merge_sort(mid+1…r))

三、🐶 归并排序的js代码

function mergeSort(arr) {  
  if (arr.length = 1) {  
    return arr;  
  }  
    
  const mid = Math.floor(arr.length / 2);  
  const left = arr.slice(0, mid);  
  const right = arr.slice(mid);  
  
  return merge(mergeSort(left), mergeSort(right));  
}  
  
function merge(left, right) {  
  let result = [];  
  // leftIndex和rightIndex2个游标分别指向左🈶右数组的第一个元素
  let leftIndex = 0;  
  let rightIndex = 0;  
  
  while (leftIndex < left.length && rightIndex < right.length) {  
    if (left[leftIndex] <= right[rightIndex]) {  
      result.push(left[leftIndex]);  
      leftIndex++;  
    } else { 
      result.push(right[rightIndex]);  
      rightIndex++;  
    }  
  }  
  
  // 如果左侧数组还有剩余元素,直接添加到结果中  
  while (leftIndex < left.length) {  
    result.push(left[leftIndex]);  
    leftIndex++;  
  }  
  
  // 如果右侧数组还有剩余元素,直接添加到结果中  
  while (rightIndex < right.length) {  
    result.push(right[rightIndex]);  
    rightIndex++;  
  }  
  
  return result;  
}  
  
// 使用示例  
let arr = [5, 3, 8, 4, 2, 7, 1, 6];  
let sortedArr = mergeSort(arr);  
console.log(sortedArr); // 输出: [1, 2, 3, 4, 5, 6, 7, 8]

四、🐶 归并排序的性能分析

归并排序是稳定的排序算法吗?

上面代码第20行,当左侧代码等于右侧的时候,我们先push左侧的代码到临时数组result,这样就保证了值相同的元素,在合并前后的先后顺序不变。所以,归并排序是一个稳定的排序算法。

归并排序的时间复杂度是多少?

归并排序的执行效率与要排序的原始数组的有序程度无关,所以其时间复杂度是非常稳定的,不管是最好情况、最坏情况,还是平均情况,时间复杂度都是 O(nlogn)。

归并排序的空间复杂度是多少?

归并排序并没有像快排那样,应用广泛,这是为什么呢?因为它有一个致命的“弱点”,那就是归并排序不是原地排序算法。这是因为归并排序的合并函数,在合并两个有序数组为一个有序数组时,需要借助额外的存储空间。在任意时刻,CPU 只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过 n 个数据的大小,所以空间复杂度是 O(n)。