侧边栏壁纸
博主头像
Fonda's Lab 博主等级

关山难越,谁悲失路之人?萍水相逢,尽是他乡之客。

  • 累计撰写 49 篇文章
  • 累计创建 27 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

归并排序

LouisFonda
2024-01-16 / 0 评论 / 0 点赞 / 6 阅读 / 0 字 / 正在检测是否收录...

归并排序

希尔排序给我们带来了一个新思路,将一个问题拆分成几个小规模的子问题,然后用现成的方案解决这些子问题,再慢慢合并问题来解决原问题。归并排序就是采用这种思想的算法。

思路

归并排序会一次性把一个很大的数组分成若干个长度为1的数组,长度为一的数组当然也是有序的,这个时候怎么合并就是解决问题的核心,由于所有的长度为1的数组是有序的,这个时候我们只要把两个长度为1的数组合并成1个长度为2的有序数组,再把两个长度为二的数组合并成1个长度为4的数组,直到两个长度为n/2的数组合并成一个长度为n的数组,这个时候我们就还原了原数组,要想原数组变得有序,只需要在合并的时候做一下比较即可,比如说[1,3],[2,4]这个数组我们可以使用两个指针left和right分别指向第一个数组的1和第二个数组的2,比较left是否小于right如果小于就把left对应的值push到新数组中去,之后left++再比较如果大于就把right对应的值push到数组中去,直到left等于数组长度,或者right等于数组长度,再把剩余的内容全部push到新数组中即可,这个时候合并完成的数组是有序的。

归并排序动画.gif

代码

/**
 * @description 不修改原数组,返回新数组
 * @param {number[]} left - 需要合并的左数组
 * @param {number[]} right - 需要合并的右数组
 */
export function 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);

  // 递归地对左右两部分进行归并排序
  const sortedLeft = mergeSort(left);
  const sortedRight = mergeSort(right);

  // 合并
  return merge(sortedLeft, sortedRight);
}

function merge(left, right) {
  const result = [];
  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++;
    }
  }

  // 将剩余的元素加入结果数组
  return result.concat(left.slice(leftIndex), right.slice(rightIndex));
}

export default mergeSort;

我们可以发现归并排序没有修改元数组,而是返回了一个新的数组,如果想要修改原数组可以遍历新数组把对应的值都插进去,但是这样时间复杂度就上来了,没必要。

总结

归并排序和我们之前的排序有非常大的区别,首先归并排序并没有在原数组中交换变量,而是借用了其他的数组来保存变量,这样会导致归并排序的空间复杂度会明显高于其他的排序,但是空间换时间是值得的。如果你细心的话就会发现,归并排序并没有交换变量的过程,所以就不会存在排完序之后原本相同的两个数之前的跑到后面去,也就是说归并排序是一个O(nlogn)的算法,并且还是一个稳定排序算法,需要考虑稳定性的话可以考虑此算法,像我们熟知的火狐浏览器的Array.prototype.sort()的核心实现就是归并排序,而google使用的是快速排序+冒泡。

0

评论区