排序算法之——快速排序

462 阅读3分钟

前置知识

栈 Stack

堆栈, 又称为 栈 或者堆叠, 是计算机科学中的一种数据结构, 而且这种数据结构, 只能在栈顶对数据进行 插入 和 取出(删除), 后进先出 LIFO Last in First Out

可以参考现实生活中的例子:比如弹夹,最后进去的子弹, 最先被发射出去

image.png

调用栈 Call Stack

MDN: 调用栈是解释器(比如浏览器中的 JavaScript 解释器)追踪函数执行流的一种机制。当执行环境中调用了多个函数时,通过这种机制,我们能够追踪到哪个函数正在执行,执行的函数体中又调用了哪个函数。

  • 每调用一个函数,解释器就会把该函数添加进调用栈并开始执行。
  • 正在调用栈中执行的函数还调用了其它函数,那么新函数也将会被添加进调用栈,一旦这个函数被调用,便会立即执行。
  • 当前函数执行完毕后,解释器将其清出调用栈,继续执行当前执行环境下的剩余的代码。
  • 当分配的调用栈空间被占满时,会引发“堆栈溢出”错误
function greeting () {
  sayHi()
}
function sayHi () {}
greeting()
  • 当 解释器 调用 greeting 函数时, 会将 greeting 压入到栈中, 并且执行
  • 当 解释器 又调用 sayHi 函数时, 会将 sayHi 压入到 栈中, 并且执行
  • sayHi 函数 执行完毕后, 会从 栈 中被 清除出去
  • 执行 greeting 的函数

image.png

递归

所谓的递归,指的是函数定义中使用函数自身的方法, 换句话说就是 自己调用自己

小时候有一个故事: 从前有座山, 山上有座庙,庙里住着一个老和尚和小和尚, 有一天老和尚给小和尚讲故事, 故事内容是: 从前有座山, 山上有座庙,庙里住着一个老和尚和小和尚, 有一天老和尚给小和尚讲故事... 就这样无限的循环

但是 我们的递归, 它并非无休止的循环下去, 而是需要中止的。 所以在编写递归函数的时候, 得告诉它什么时候终止递归. 也就是基线条件递归条件

基线条件: 告诉函数不再自我调用 递归条件: 函数自我调用

欧几里德算法

欧几里德算法 又称为 辗转相除法, 计算两个正整数的a, b的最大公约数; 比如: 计算 A = 270, B = 192 的最大公约数

  • 第一步: AB=270192=1...78\frac{A}{B} = \frac{270}{192} = 1...78

  • 第二步: B78=19278=2...36\frac{B}{78} = \frac{192}{78} = 2...36

  • 第三步: 7836=2...6\frac{78}{36} = 2...6

  • 第四步: 366=6...0\frac{36}{6} = 6...0 那么 A = 270, B = 192 的最大公约数就是: 6

快速排序

分而治之(Divide and Conquer 简称 D&C): 将一个序列分为 较大 和 较小两个子序列, 然后递归排序两个子序列

步骤:

  1. 挑选 基准值
  2. 将序列 根据 挑选出来的 基准值, 进行 分成 较大较小两个子序列
  3. 递归 两个子序列, 按照1-2-3的顺序 执行下去

时间复杂度

[1, 2, 3, 4, 5, 6, 7, 8, 9], 每次都以第一个元素 为基准值, 那么调用的高度为8次O(n)O(n),但是 如果每次都以 中间的元素 作为 基准值, 那么 调用高度 为4次O(log2n)O(log_2n), 每一层都要循环遍历, 所需的时间 O(n)O(n) 最好时间复杂度: O(nlog2n)O(nlog_2n) 最差时间复杂度: O(n2)O(n^2)

图解

image.png

代码实现(Typescript版)

function quickSort (arr: Array<Number>): Array<Number> {
  if (arr.length < 2) return arr // 0 或者 1个
  const lessArr: Array<Number> = []
  const greaterAfr: Array<Number> = []
  const base = arr[0]
  for (let i = 1; i < arr.length; i++) {
    if (arr[i] < base) {
      lessArr.push(arr[i])
    } else{
      greaterAfr.push(arr[i])
    }
  }
  return [...quickSort(lessArr), base, ...quickSort(greaterAfr)]
}
const arr:Array<Number> = [8, 5, 2, 6, 9, 3, 1, 4, 0, 7]
console.log(quickSort(arr)) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]