特性:
排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|
快速排序 | O(nlogn) |
O(logn) |
不稳定 |
和归并排序的对比
快速排序 | 归并排序 | |
---|---|---|
特点 | 两个子数组都有序时,则整个数组也就自然有序了 | 将数组分成两个子数组分别排序,并将有序的子数组归并 |
切分 | 取决于数组的内容 | 等分两半 |
思路:
选取一个“切分“点,确保:
- 切分点的左侧都小于它。
- 切分点的右侧都大于它。
- 分别对切分点进行排序
过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据比另一部分的所有数据要小,再按这种方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,使整个数据变成有序序列。
伪代码:
function quickSort(a, lo, hi) {
if (hi <= lo) return;
const j = partition(a, lo, hi); // 切分,快速排序的关键
quickSort(a, lo, j - 1);
quickSort(a, j + 1, hi);
}
切分
const { less, exch } = require('./util');
function partition(a, lo, hi) {
let i = lo;
let j = hi + 1;
// 随意选取a[lo]做为切分元素
const v = a[lo];
while(1) {
// 从数组的左端开始向右扫描,直到寻找到一个大于等于v的元素
while(less(a[++i], v])) if(i === hi) break;
// 从数组的右端开始向左扫描,直到寻找到一个小于等于v的元素
while(less(v, a[--j])) if (j === lo) break;
// 边界条件:如果指针相遇,则退出循环
if(i >= j) break;
// 调换他们的位置,一定可以找到,如果找不到则在上一步终止了
exch(a, i, j);
}
// 交换v和指针相遇位置的值
exch(a, lo, j);
return j;
}
实现
const { less, exch, getArr } = require('../utils');
function partition(arr, lo, hi) {
let i = lo;
let j = hi + 1;
// 随意选取一个作为“切分”点
const v = arr[lo];
while (1) {
// 从左到右寻找到大于 v 的值(下标)
while (less(arr[++i], v)) {
if (i === hi) break;
}
// 从右到左寻找到小于 v 的值(下标)
while (less(v, arr[--j])) {
if (j === lo) break;
}
// 如果指针(下标)对撞则终止循环
if (i >= j) break;
// 交换小的值
exch(arr, i, j);
}
exch(arr, lo, j);
return j;
}
function quickSort(arr, lo, hi) {
if (lo === undefined) {
lo = 0;
hi = arr.length - 1;
}
if (lo >= hi) return;
const j = partition(arr, lo, hi); // 获取切分点
quickSort(arr, lo, j - 1);
quickSort(arr, j + 1, hi);
return arr;
}
算法改进
切换到插入排序
小数组使用使用插入排序吧,长度在5-15内用这个,很快;
三取样切分
使用子数组的一小部分元素的中位数来切分数组,代价是需要计算中位数。
三向切分的快速排序
针对重复数据比较多的数组。
function quick3way(a, lo, hi) {
if (hi <= lo) return;
let lt = lo; // a[lo, ...lt - 1]中的所有元素都小于v
let gt = hi; // a[gt + 1, ...hi]中的所有元素都大于v
let i = lo + 1; // a[lt, ...i]中的所有元素都等于v
// a[i, ...gt] 中的元素还未确定
let v = a[lo];
while(i <= gt) {
let cmp = a[i] - v;
// a[i] < v,交换 a[lt] 和 a[i],lt指针、i指针右移,将小于v的都放到v的右边
if (cmp < 0) exch(a, lt++, i++);
// a[i] > v,交换 a[i] 和 a[gt],gt左移,将大于v的都迁移到v的右边
else if (cmp > 0) exch(a, i, gt--);
// 相等情况移动i指针
else i++
}
sort(a, lo, lt - 1);
sort(a, gt + 1, hi);
}