排序算法 Note-FrontEnd-20

151 阅读5分钟

排序算法的相关知识,内容包括小试牛刀、算法入门、常见排序算法。

一、小试牛刀

任务 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

我发现一个一个零散的小知识我懂,当结合在一块后,用实际的语言表达出来就难了,一起来一段一段分析一下

  1. a,b 是函数 swap 的形式参数,这个函数把 a 和 b 值的内容,做了一次交换
  2. 当写下swap(c,d)调用这个函数的时候,首先 c,d 的值被复制了一份给 a,b
  3. 然后 a,b 值做一次交换
  4. 函数调用完 a,b 形参消失,程序结束

c,d 的值根本没有反应啊,先复习一下函数的知识和 this、 argunents。

  1. 形参可多可少,形参只是给参数取名字,没有实际的功能
  2. arguments 和 this 每个函数都有,除了箭头函数
  3. 如果你传的 this 不是对象,JS 会自动帮你封装成对象
  4. fn(1,2,3) 那么 arguments 就是 [1,2,3] 伪数组
  5. 目前可以用 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

「@浪里淘沙的小法师」