背景: 前段时间, 某前端校招二面上来就让写快排, 当时就懵了, 想着很久以前看过的, 忘了, 血亏. 最后是挂了, 可能第一印象就不行. 现在记录一下, 也避免大家遇到这种情况
插入排序
思想
二分查找并插入元素
- 对于有序数组, 插入一个值使得数组仍然有序
- 选择有序数组中间元素, 将有序数组分成前后两部分
- 比较待插入元素与中间元素
- 待插入元素大于中间元素则插入后半部分, 否则插入前半部分
- 插入前/后半部分的方式重复步骤2, 3, 4
排序
- 有一个待排序数组, 和一个作为容器的空数组
- 从待排序数组中选择一个元素插入到容器数组, 并保持其有序
代码
const numbers = [2, 4, 6, 12, 43, 12, 56, 1]
function insertionSort(arr) {
function binInsert(val, arr) {
if (arr.length === 0) {
return [val]
} else if (arr.length === 1) {
return val > arr[0] ? [arr[0], val] : [val, arr[0]]
} else {
const middle = Math.floor(arr.length / 2)
return val > arr[middle] ?
[...arr.slice(0, middle), ...binInsert(val, arr.slice(middle))] :
[...binInsert(val, arr.slice(0, middle)), ...arr.slice(middle)]
}
}
return arr.reduce((buf, val) => binInsert(val, buf), [])
}
console.log(insertionSort(numbers))
归并排序
思想
先分解, 后归并
归并
- 有两个有序数组(从小到大), 和一个作为容器的空数组
- 各从两数组头部弹出一个元素
- 比较两个弹出元素大小, 较小的元素从容器尾部推入, 较大元素放回原数组
- 重复步骤2, 3, 直至两个有序数组之一为空
- 当两个有序数组之一为空时, 将非空数组的头部与容器数组的尾部连接成一个新数组
- 新的数组即为排序好的数组
分解
- 将待排序数组分为两个数组
- 将分出来的两个数组再各自分成两个数组
- 重复步骤2, 直至分成的数组只有两个元素或更少
- 对于只有两个元素的数组, 通过比较即可排序
- 归并各组分解出来的数组
代码
const numbers = [2, 4, 6, 12, 43, 12, 56, 1]
function mergeSort(arr) {
if (arr.length < 2) {
return arr
} else if (arr.length === 2) {
return arr[0] > arr[1] ?
[arr[1], arr[0]] :
[arr[0], arr[1]]
}
else {
const buf = []
const middle = Math.floor(arr.length / 2)
const part0 = mergeSort(arr.slice(0, middle))
const part1 = mergeSort(arr.slice(middle))
for (let i = 0, j = 0; i < part0.length || j < part1.length;) {
const val0 = part0[i]
const val1 = part1[j]
if (val0 && val1) {
if (val0 < val1) {
buf.push(val0)
i++
} else {
buf.push(val1)
j++
}
} else if (val0) {
buf.push(val0)
i++
} else if (val1) {
buf.push(val1)
j++
}
}
return buf
}
}
console.log(mergeSort(numbers))
冒泡排序
思想
- 有一个待排序数组, 一个空的数组作为容器
- 对于待排序数组, 从头至尾, 两两比较元素, 交换位置(小的在前)
- 步骤2使得待排序数组中最大元素位于最后
- 最大元素从容器数组头部推入
- 最大元素之前的所有元素构成一个新的待排序数组
- 重复步骤2, 3, 4, 5, 直至新的待排序数组只含一个元素
- 将这个新的待排序数组的唯一元素从容器数组头部推入
代码
const numbers = [2, 4, 6, 12, 43, 12, 56, 1]
function bubbleSort(arr) {
if (arr.length < 2) {
return arr
} else {
for (let i = 1; i < arr.length; i++) {
if (arr[i] < arr[i - 1])
[arr[i - 1], arr[i]] = [arr[i], arr[i - 1]]
}
return [...bubbleSort(arr.slice(0, -1)), ...arr.slice(-1)]
}
}
console.log(bubbleSort(numbers))
快速排序
思想
- 有一个待排序数组
- 从待排序数组中选择一个元素作为
基准值 - 遍历待排序数组, 小于基准值的元素构成一个数组(
less), 大于基准值的元素也构成一个数组(greater) - 如果
less和greater都已经排序好了, 连接less,基准值,greater即得到最终排序好的数组 - 对待排序数组分解出来的
less和greater分别应用步骤2, 3, 4
代码
const numbers = [2, 4, 6, 12, 43, 12, 56, 1]
function quickSort(arr) {
if (arr.length < 2) {
return arr
} else {
const pivot = arr[0], less = [], greater = []
for (let i = 1; i < arr.length; i++) {
const num = arr[i];
num > pivot ? greater.push(num) : less.push(num)
}
return [...quickSort(less), pivot, ...quickSort(greater)]
}
}
console.log(quickSort(numbers))
睡眠排序
四大xxxx有五个显然是常识
思想
- 有一个待排序数组, 一个空的数组作为容器
- 对于待排序数组每个元素, 使用元素的值作为时间, 设置一个相应的定时器
- 定时器归零时, 将对应元素从容器数组尾部推入
代码
const numbers = [2, 4, 6, 12, 43, 12, 56, 1]
async function sleepSort(arr) {
const sorted = []
await Promise.all(numbers.map(t => new Promise((res) => {
const timer = setTimeout(() => {
clearTimeout(timer)
sorted.push(t)
res()
}, t)
})))
return sorted
}
sleepSort(numbers).then(sorted => console.log(sorted))