排序算法 时间复杂度 空间复杂度 是否稳定
冒泡排序 O(n2)
插入排序 O(n2)
选择排序 O(n2)
归并排序 O(nlogn) O(n) 稳定
快速排序 O(nlogn) O(1) 不稳定

冒泡排序

插入排序

选择排序

归并排序

分治思想,分治算法一般都是用递归来实现的。
写递归的技巧:分析得出递推公式,然后找到终止条件,最后将递推公式翻译成递归代码。
归并排序不是原地排序算法。
在任意时刻,CPU只会有一个函数在执行,也就只会有一个临时的内存空间在使用。临时内存空间最大也不会超过n个数据的大小,所以空间复杂度是O(n)

  1. function foo(arr) {
  2. const merge_sort = (left, right) => {
  3. // 递归结束条件:
  4. if(left >= right) return
  5. // 这样可以避免加法越界
  6. let pivot = left + Math.floor((right-left)/2)
  7. merge_sort(left, pivot)
  8. merge_sort(pivot+1, right)
  9. //这里要注意!输入的是节点
  10. merge(left, right, pivot)
  11. }
  12. const merge = (left, right, pivot) => {
  13. let temp = [], i = left, j = pivot + 1, k = 0
  14. // 注意中间是&&
  15. while(i<=pivot && j<=right) {
  16. if(arr[i] <= arr[j]){
  17. temp[k] = arr[i]
  18. i++
  19. k++
  20. } else {
  21. temp[k] = arr[j]
  22. j++
  23. k++
  24. }
  25. }
  26. // 找到是哪一个数组没被遍历完,直接接到temp后面
  27. // 注意等号
  28. let start = i <= pivot ? i : j
  29. let end = i <= pivot ? pivot : right
  30. while(start<=end) {
  31. temp[k++] = arr[start++]
  32. }
  33. // 注意这里arr要从left开始
  34. for(let i=0; i<=right-left; i++){
  35. arr[left+i] = temp[i]
  36. }
  37. }
  38. merge_sort(0, arr.length-1)
  39. return arr
  40. }

快速排序

快排核心思想:

  1. function foo1(arr) {
  2. const quick_sort = (left, right) => {
  3. if(left>=right) return
  4. let pivot = build(left, right)
  5. quick_sort(left, pivot-1)
  6. quick_sort(pivot+1, right)
  7. }
  8. const build = (left, right) => {
  9. let pivot = arr[right]
  10. let i = left
  11. // 这里pivot等于数组最后一个值,遍历数组时要去掉这个pivot
  12. for(let j=left; j<=right-1; j++){
  13. if(arr[j] < pivot) {
  14. [arr[i], arr[j]] = [arr[j], arr[i]]
  15. i++
  16. }
  17. }
  18. // arr[right]最后要和arr[i]交换,因为左边[0,i-1]区间都是小于pivot的值
  19. [arr[right], arr[i]] = [arr[i], arr[right]]
  20. return i
  21. }
  22. quick_sort(0, arr.length-1)
  23. return arr
  24. }