本节的两种排序算法:归并排序和快速排序都基于递归实现,平均时间复杂度都为 O(nlgn)

归并排序

归并思想:

  • 数组分成两个子数组(通常从中间分)
  • 子数组也继续分(递归)直到只有一个元素(终止条件)

归并排序还用到了合并两个有效数组的工具函数,基于“双指针法”实现

  1. function mergeArr(arr1, arr2) { // 合并两个有序数组,返回一个新数组
  2. const len1 = arr1.length
  3. const len2 = arr2.length
  4. const res = []
  5. let i = 0 // 两个指针分别指到 arr1, arr2
  6. let j = 0
  7. while (i < len1 && j < len2) { // 两个数组都有值,按顺序填
  8. if (arr1[i] < arr2[j]) {
  9. res.push(arr1[i])
  10. i++
  11. } else {
  12. res.push(arr2[j])
  13. j++
  14. }
  15. }
  16. if (i < len1) {// arr2填完了,arr1还有
  17. return res.concat(arr1.slice(i))
  18. } else {
  19. return res.concat(arr2.slice(j))
  20. }
  21. }
function mergeSort(arr) {
  const len = arr.length
  if (len <= 1) { // 递归边界条件,分到数组里只有一个元素
    return arr
  }
  const mid = Math.floor(len / 2)
  const sortedLeft = mergeSort(arr.slice(0, mid))
  const sortedRight = mergeSort(arr.slice(mid))// 省略第2个参数默认到数组末尾
  return    mergeArr(sortedLeft, sortedRight)
}

平均时间复杂度 O(nlgn)

快速排序

快排思想:

  • 选择一个元素作为”基准”,通常取中间的元素。
  • 所有小于”基准”的元素,都移到”基准”的左边;所有大于”基准”的元素,都移到”基准”的右边。
  • (递归)对”基准”左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止(终止条件)。

    参考:快排的js实现——阮一峰

function quickSort(arr){ 
  const len = arr.length
  if(len <= 1){ // 递归,先写边界条件
    return arr
  }
  const midIndex = Math.floor(len/2) // 确定基准元素
  const left = []
  const right = []
  for(let i=0; i<len;i++){
    if(i === midIndex){ // 跳过基准元素不处理
      continue
    }
    if(arr[i] < arr[midIndex]){
      left.push(arr[i])
    }else{
      right.push(arr[i])
    }
  }
  return [...quickSort(left), arr[midIndex], ...quickSort(right)]
}

这种实现没有操作原数组,而是返回一个新数组,空间复杂度较高 O(n)
平均时间复杂度 O(nlgn)