亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • 刷题

    • 算法
    • 排序
      • 冒泡
      • 选择
      • 插入
      • 快排
      • 归并
      • 二叉树遍历
    • LeetCode
  • 杂(待整理)

  • 学习

  • 面试

  • 实用技巧

  • 心情杂货

  • 年度总结

  • 友情链接
  • 更多
  • 刷题
0zcl
2025-06-19
目录

排序

# 冒泡

function bubbleSort(arr) {
  for (let i=0; i<arr.length-1; i++) {
    for (let j=1; j<=arr.length-1-i; j++) {
      if (arr[j] < arr[j-1]) {
        [arr[j], arr[j-1]] = [arr[j-1], arr[j]]
      }
    }
  }

  return arr
}

bubbleSort([1, 3, 5, 2, 8, 5, 1])
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 外层循环:每次遍历后,最大的元素会“冒泡”到数组末尾。需要处理len-1次就能把数组排好序。
  2. 内层循环:内层循环用于两两比较,值大的放后面。内层循环的范围会随着外层循环的进行而减少。内循环遍历len-1-i
  • 时间复杂度:O(n^2^)
  • 空间复杂度:O(1)

优化:O(n) 当数组有序时,冒泡排序时间复杂序为O(n) 思路:如果在内层循环中没有发生任何交换,说明数组已经排序完成,可以提前结束排序过程。

function bubbleSort(arr) {
  for (let i=0; i<arr.length-1; i++) {
    let swapped = false
    for (let j=1; j<=arr.length-1-i; j++) {
      if (arr[j] < arr[j-1]) {
        swapped = true
        [arr[j], arr[j-1]] = [arr[j-1], arr[j]]
      }
    }
    if (!swapped) break
  }

  return arr
}

bubbleSort([1, 2, 3])
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 时间复杂度:O(n^2^)
    • 最坏情况:O(n^2^)
    • 最好情况:O(n)。当数组已经排序时,只需要进行一次遍历即可完成排序
  • 空间复杂度:O(1)

# 选择

function selectSort(arr) {
  for (let i=0; i<arr.length; i++) {
    for (let j=1+i; j<arr.length; j++) {
      if (arr[j] < arr[i]) {
        [arr[i], arr[j]] = [arr[j], arr[i]]
      }
    }
  }
  return arr
}

arr = [5,4,3,2,1,6]
selectSort(arr)
1
2
3
4
5
6
7
8
9
10
11
12
13
  1. 外层循环:每次处理后,使i是i之后数据的最小值。
  2. 内层循环:内层循环用于两两比较,值小的放前面。
  • 时间复杂度:O(n^2^)
  • 空间复杂度:O(1)

# 插入

image

插入排序的工作步骤:

  1. 从第一个元素开始,该元素可以认为已经被排序。
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
  5. 将新元素插入到该位置后。
  6. 重复步骤2~5。
function insertSort(arr) {
  for (let i=1; i<arr.length; i++) {
    let j = i-1
    let tempVal = arr[i]
    while (j >=0 && tempVal < arr[j]) {
      arr[j+1] = arr[j]
      j--
    }
    arr[j+1] = tempVal
  }
  return arr
}


let arr = [1, 3, 5, 2, 8, 5, 1]
insertSort(arr)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. 头指针指向数组下标i。arr[i]插入下标0到i-1的有序数组中
  2. 要用tempVal暂存要插入的值。因为在插入比对的过程中,数据后移,会覆盖
  • 时间复杂度:O(n^2^)
    • 最坏情况:O(n^2^)
    • 最好情况:O(n)。当数组已经是有序时,每个元素只需要与前一个元素进行比较,不需要移动
    • 平均情况:O(n^2^)
  • 空间复杂度:O(1)

# 快排

function quickSort(arr, head, tail) {
  if (head >= tail) return

  const quickSortDetail = (head, tail) => {
    while (head < tail) {
      while (arr[tail] >= arr[head] && head < tail) { // 1
        tail--
      }
      [arr[tail], arr[head]] = [arr[head], arr[tail]]
      while (arr[head] <= arr[tail] && head < tail) { // 2
        head++
      }
      [arr[tail], arr[head]] = [arr[head], arr[tail]]
    }

    return head
  }
  
  const key = quickSortDetail(head, tail)
  quickSort(arr, head, key-1)
  quickSort(arr, key+1, tail)
  return arr
}

let arr = [1, 3, 5, 2, 8, 5, 1]
// let arr = [6,22, 4,5,7,7888,32]
quickSort(arr, 0, arr.length-1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  1. 一次快排:左边不大于基准值;右边不小于基准值。返回一次快排后基准值下标
  2. 头指针head,尾指针tail。初始设基准值为头指针的值:temp = arr[head]。 比较,不断交换头尾指针,直到相遇
  3. 1,2 至少要有一处 >= , <=, 否则会死循环
  4. 这种做法,头尾指针必有一个是基准值。对比头尾指针,也是和基准值对比,达到左边不大于基准值;右边不小于基准值
// 测试 [6, 22, 4, 5, 7, 7888, 32]
[6, 22, 4, 5, 7, 7888, 32] head 0 tail 6
[6, 22, 4, 5, 7, 7888, 32] head 0 tail 3
[5, 22, 4, 6, 7, 7888, 32] head 0 tail 3 swap
[5, 22, 4, 6, 7, 7888, 32] head 1 tail 3
[5, 6, 4, 22, 7, 7888, 32] head 1 tail 3 swap
[5, 6, 4, 22, 7, 7888, 32] head 1 tail 2
[5, 4, 6, 22, 7, 7888, 32] head 1 tail 2 swap
[5, 4, 6, 22, 7, 7888, 32] head 2 tail 2
// 返回key为2

// 测试 [6, 22, 4, 7, 7888, 4, 32]
[6, 22, 4, 7, 7888, 4, 32] head 0 tail 6
[6, 22, 4, 7, 7888, 4, 32] head 0 tail 5 tail--
[4, 22, 4, 7, 7888, 6, 32] head 0 tail 5 swap
[6, 22, 4, 7, 7888, 4, 32] head 1 tail 5 head++
[6, 4, 4, 7, 7888, 22, 32] head 1 tail 5 swap
[6, 4, 4, 7, 7888, 22, 32] head 1 tail 2 tail--

// 这就是为什么 快排代码1,2 至少要有一处 >= , <=, 否则两个4会一直在交换,死循环,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 时间复杂度:O(nlog(n))。O(log(n))次快排,每次O(n) ==> O(log(n)) * O(n) ==> O(nlog(n))

# 归并

function arrayMerge(left, right) {
  let result = []
  while (left.length && right.length) {
    if (left[0] < right[0]) {
      result.push(left.shift())
    } else {
      result.push(right.shift())
    }
  }
  if (left.length) {
    result = result.concat(left)
  } else {
    result = result.concat(right)
  }
  return result
}

function mergeSort(arr) {
  if (arr.length <= 1) return arr
  const middleIndex = Math.floor(arr.length/2)

  const left = mergeSort(arr.slice(0, middleIndex))
  const right = mergeSort(arr.slice(middleIndex))

  return arrayMerge(left, right)
}



let arr = [1, 3, 5, 2, 8, 5, 1]
console.log(mergeSort(arr))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  1. 先一分二,直至只有一个元素;再组合,数组合并
mergeSort([1, 3, 5, 2, 8, 5, 1])
  -> mergeSort([1, 3, 5])
      -> mergeSort([1]) -> [1]
      -> mergeSort([3, 5])
          -> mergeSort([3]) -> [3]
          -> mergeSort([5]) -> [5]
          -> arrayMerge([3], [5]) -> [3, 5]
      -> arrayMerge([1], [3, 5]) -> [1, 3, 5]
  -> mergeSort([2, 8, 5, 1])
      -> mergeSort([2, 8])
          -> mergeSort([2]) -> [2]
          -> mergeSort([8]) -> [8]
          -> arrayMerge([2], [8]) -> [2, 8]
      -> mergeSort([5, 1])
          -> mergeSort([5]) -> [5]
          -> mergeSort([1]) -> [1]
          -> arrayMerge([5], [1]) -> [1, 5]
      -> arrayMerge([2, 8], [1, 5]) -> [1, 2, 5, 8]
  -> arrayMerge([1, 3, 5], [1, 2, 5, 8]) -> [1, 1, 2, 3, 5, 5, 8]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 二叉树遍历

      A
     / \
    B   C
   / \   \
  D   E   F

const root = {
  val: "A",
  left: {
    val: "B",
    left: {
      val: "D"
    },
    right: {
      val: "E"
    }
  },
  right: {
    val: "C",
    right: {
      val: "F"
    }
  }
}

function preOrder(root) {
  if (!root) return
  console.log(root.val)
  preOrder(root.left)
  preOrder(root.right)
}

function middleOrder(root) {
  if (!root) return
  middleOrder(root.left)
  console.log(root.val)
  middleOrder(root.right)
}

function postOrder(root) {
  if (!root) return
  postOrder(root.left)
  postOrder(root.right)
  console.log(root.val)
}

// 测试
preOrder(root) // A B D  E C F
middleOrder(root) // D B E A C F
postOrder(root) // D E B F C A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

x序遍历,指根结点【前、中、后】

  • 前:根左右
  • 中:左根右
  • 后:左右根

参考: 十大排序算法超全大综合,动图演示,你真的值得拥有! (opens new window)

编辑 (opens new window)
上次更新: 2025/07/20, 06:21:22
算法
LeetCode

← 算法 LeetCode→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式