算法入门:请写一个 min 函数,要求 min(numbers) 能返回数组 numbers 中的最小数字
let min = (numbers) => {
if (numbers.length > 2) {
return min([numbers[0], min(numbers.splice(1))])
} else {
return Math.min.apply(null, numbers)
}
}
console.log('取[16, 12, 13, 7, 25]最小值:')
console.log(min([16, 12, 13, 7, 25]))
1. 选择排序
思路:每次选择最小/大的,选完就排完。
- 递归写法
// 请写出一个 sort 函数,要求 sort(numbers) 能返回一个把 numbers 从小到大排列的数组
// 获取最小值的index
let minIndex = (numbers) => {
let index = 0
for (let i=1; i<numbers.length;i++){
if (numbers[i] < numbers[index]) index = i
}
return index
}
let sort = (numbers) => {
if (numbers.length > 2) {
let index = minIndex(numbers)
let min = numbers[index]
numbers.splice(index, 1)
return [min].concat(sort(numbers))
} else {
return numbers[0] < numbers[1] ? numbers : numbers.reverse()
}
}
console.log('排序[17,39,24,15,11]:')
console.log(sort([17,39,24,15,11]))
- 循环写法
// 获取最小值的index
let minIndex = (numbers) => {
let index = 0
for (let i=1; i<numbers.length;i++){
if (numbers[i] < numbers[index]) index = i
}
return index
}
// 替换数组两个下标对应数据的位置
let swap = (array, i, j) => {
let temp = array[i]
array[i] = array[j]
array[j] = temp
}
let sort2 = numbers => {
for (let i = 0; i < numbers.length - 1; i++) {
let index = minIndex(numbers.slice(i)) + i
if (index !== i) {
swap(numbers, index, i)
}
}
return numbers
}
console.log('排序[4,3,6,8,2]:')
console.log(sort2([4,3,6,8,2]))
2. 快速排序
思路:重复做操作「以某数为基准,小的去前面,大的去后面」
- 递归写法 参照阮一峰的写法
let quickSort = numbers => {
if (numbers.length > 1) {
// 基准的index
let pivotIndex = Math.floor(numbers.length / 2)
// 基准的值
let pivot = numbers.splice(pivotIndex, 1)[0]
let left = []
let right = []
for (let i = 0; i < numbers.length; i++) {
if (numbers[i] < pivot) {
left.push(numbers[i])
}else{
right.push(numbers[i])
}
}
return quickSort(left).concat([pivot],quickSort(right))
} else {
return numbers
}
}
console.log("排序[8,3,9,4,2]")
console.log(quickSort([8,3,9,4,2]))
3. 归并排序
思路:左边一半排好序,右边一半排好序,然后把左右两边合并起来。
let mergeSort = numbers => {
let k = numbers.length
if (k === 1) {
return numbers
}
let left = numbers.slice(0, Math.floor(k / 2))
let right = numbers.slice(Math.floor(k / 2))
return merge(mergeSort(left), mergeSort(right))
}
let merge = (a,b) => {
if (a.length === 0) return b
if (b.length === 0) return a
return a[0] > b[0] ? [b[0]].concat(merge(a,b.slice(1))) : [a[0]].concat(merge(a.slice(1),b))
}
console.log("排序[18,3,9,4,12]")
console.log(mergeSort([18,3,9,4,12]))
4. 计数排序
思路:用一个哈希表做记录,发现数字 N 就记 N:1,如果再次发现 N 就加 1,最后把哈希表的 key 全部打出来,假设 N:m,那么 N 需要打印 m 次。
let countSort = arr => {
let hashTable = {}, max = 0, result = []
// 遍历数组
for (let i = 0; i < arr.length; i++) {
if (!(arr[i] in hashTable)) {
hashTable[arr[i]] = 1
} else {
hashTable[arr[i]] += 1
}
if (arr[i] > max) max = arr[i]
}
// 遍历哈希表
for (let j = 0; j <= max; j++) {
if (j in hashTable) {
for (let i = 0; i < hashTable[j]; i++) {
result.push(j)
}
}
}
return result
}
console.log("排序[18,3,9,4,9,12]")
console.log(countSort([18,3,9,4,9,12]))
5. 时间复杂度对比
- 选择排序 O(n^2)
- 快速排序 O(n log2 n)
- 归并排序 O(n log2 n)
- 计数排序 O(n + max) 「用空间换时间」
6. 其他排序
- 冒泡排序 visualgo.net/zh/sorting
- 插入排序 visualgo.net/zh/sorting 点击INS
- 希尔排序 sorting.at/ 选择Shell Sort
- 基数排序 visualgo.net/zh/sorting 点击RAD