排序算法的相关知识,内容包括小试牛刀、算法入门、常见排序算法。
一、小试牛刀
任务 1 - 找出最小值
当我们想到用数组表示数字,其实就在使用数据结构
// 这段代码没有实现递归,通过 console.log 调试大法,发现了错误行
let min = numbers=>{
if (numbers.length>2){
return min([numbers[0],min([numbers.slice(1)])]) // '这行代码有误'
}else{
return Math.min.apply(null,numbers)
}
}
// 我发现错误了,min 里面的 numbers.slice(1) 外再加方括号是几个意思?
// 视力为零啊,再重新手打一遍
let min = numbers=>{
if (numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else{
return Math.min.apply(null,numbers)
}
}
小知识:
let minOf2 = ([a,b])=>a<b?a:b
// 这种写法叫做析构赋值
// JS 内置了 Math.min
Math.min(1,2) // 1
Math.min.call(null,1,2)
Math.min.apply(null,[1,2])
// 关于 Math
看起来 Math 像 Object 一样是构造函数
实际上 Math 只是一个普通的对象
首字母大写是构造函数,而这是唯一的特例
递归特点:
函数不停调用自己,每次调用的参数略有不同,当满足每个简单条件时,则实现一个简单的调用,最终算出结果
可以用代入法快速理解递归,可以用调用栈快速理解递归
任务 2 - 排序
// min 函数代码使用上面即可,但是又有地方错了,小细节很难把握住啊
let sort = numbers=>{
let min = numbers=>{
if (numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else{
return Math.min.apply(null,numbers)
}
}
if (numbers.length>2){
min = min(numbers)
minIndex = numbers.indexOf(min)
numbers.splice(minIndex,1)
return sort([min].concat(numbers)) // '这行代码有误'
}else{
return numbers[0]<numbers[1]?numbers:numbers.reverse // '函数反转不加分号是几个意思?'
}
}
// 同样是递归,但是 return 的方式不一样,这个细节要知道
// 函数不加分号的事情很难察觉到啊
let sort = numbers=>{
let min = numbers=>{
if (numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else{
return Math.min.apply(null,numbers)
}
}
if (numbers.length>2){
min = min(numbers)
minIndex = numbers.indexOf(min)
numbers.splice(minIndex,1)
return [min].concat(sort(numbers))
}else{
return numbers[0]<numbers[1]?numbers:numbers.reverse()
}
}
小总结
我们用的主要方法就是数组(数据结构)和递归
二、算法入门
问:我想写一个两数交换的函数,为什么这输入的这两个参数没有交换呢?
我发现在深究代码细节的时候,有很多路还要走呀~
let swap= (a,b)=>{
let temp = a
a = b
b = temp
}
let c=1
let d=2
swap(c,d) // c 依然是 1, d 依然是 2
我发现一个一个零散的小知识我懂,当结合在一块后,用实际的语言表达出来就难了,一起来一段一段分析一下。
- a,b 是函数 swap 的形式参数,这个函数把 a 和 b 值的内容,做了一次交换
- 当写下
swap(c,d)调用这个函数的时候,首先 c,d 的值被复制了一份给 a,b - 然后 a,b 值做一次交换
- 函数调用完 a,b 形参消失,程序结束
c,d 的值根本没有反应啊,先复习一下函数的知识和 this、 argunents。
- 形参可多可少,形参只是给参数取名字,没有实际的功能
- arguments 和 this 每个函数都有,除了箭头函数
- 如果你传的 this 不是对象,JS 会自动帮你封装成对象
- fn(1,2,3) 那么 arguments 就是 [1,2,3] 伪数组
- 目前可以用 fn.call(xxx,1,2,3) 传 this 和 arguments,xxx 是 this,1,2,3 是 arguments
// 使用小白调用法打印 this, this 是 windows 对象
let swap= (a,b)=>{
console.log(this)
}
// 我想起来了,箭头函数没有 this 和 arguments,我在干嘛哟,哈哈
答:我还没有想到"通过一个函数能够达到给出两个值直接交换它们值"的方法,除非给出一个数组,那样就好办了,因为此时我修改的是他们共同指向地址的值。
let swap= numbers=>{
let temp=numbers[0]
numbers[0]=numbers[1]
numbers[1]=temp
return numbers
}
let arr =[1,2]
swap(arr)
arr // 回应:[2,1]
// 用ES6的解构赋值语法实现数据交换 (简单粗暴)
[a,b] = [b,a]
三、常见排序算法
在此总结 8 种排序方法:
1. 选择排序
2. 快速排序
3. 并归排序
4. 计数排序
5. 冒泡排序
6. 插入排序
7. 希尔排序
8. 基数排序
1. 选择排序
每次选择最小 / 大的,选完就排完
let sort = numbers=>{
let min = numbers=>{
if (numbers.length>2){
return min([numbers[0],min(numbers.slice(1))])
}else{
return Math.min.apply(null,numbers)
}
}
if (numbers.length>2){
min = min(numbers)
minIndex = numbers.indexOf(min)
numbers.splice(minIndex,1)
return [min].concat(sort(numbers))
}else{
return numbers[0]<numbers[1]?numbers:numbers.reverse()
}
}
2. 快速排序
// 快速排序
let quickSort = arr => {
if (arr.length <= 1) { return arr; }
let pivotIndex = Math.floor(arr.length / 2);
let pivot = arr.splice(pivotIndex, 1)[0];
let left = [];
let right = [];
for (let i = 0; i < arr.length; i++){
if (arr[i] < pivot) { left.push(arr[i])
} else { right.push(arr[i]) }
}
return quickSort(left).concat(
[pivot], quickSort(right) )
}
3. 并归排序
// 归并排序
let mergeSort = arr =>{
let k = arr.length
if(k===1){return arr}
let left = arr.slice(0, Math.floor(k/2))
let right = arr.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))
}
4. 计数排序
// 计数排序
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
}
5. 冒泡排序
visualgo.net/zh/sorting 选择 BUB
6. 插入排序
visualgo.net/zh/sorting 选择 INS
7. 希尔排序
sorting.at/ 选择 Shell Sort
8. 基数排序
visualgo.net/zh/sorting 选择 RAD
「@浪里淘沙的小法师」