排序算法(下)

复习任意长度的数组排序

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()
  }
}

所有递归都可以改写成循环
目前的minIndex

let minIndex = (numbers) => {
  numbers.indexOf(min(numbers))
let min = (numbers) => {
  return min(
    [numbers[0], min(numbers.slice(1))]
  )
}

写为循环

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 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) => {
    for(let i=0; i< numbers.length -1; i++){
        let index = minIndex(numbers.slice(i))+i  //为什么要加i,因为slice以后下标从0开始计,所以要把slice掉的下标加回来
        if(index!==i){swap(numbers, index, i)}
    }
    return numbers
}

错误地实现 swap

let swap = (a, b) => {
    let temp = a
    a = b
    b = temp
}
swap(numbers[1], numbers[2])

你会发现,numbers[1] 和 numbers[2] 的值原封不动
因为 a b 是简单类型,传参的时候会复制值
而正确的 swap 中的 numbers 是对象,传参复制地址
这是传值 V.S. 传址的区别

发现bug用console.log大法调试

let sort = (numbers) => {
    for(let i=0; i< numbers.length -1; i++){
    console.log(`----`) //这个log用于分割每一次循环
    console.log(`i: ${i}`)
    let index = minIndex(numbers.slice(i))+ i
    console.log(`index: ${index}`)
    console.log(`min: ${numbers[index]}`)
    if(index!==i){
        swap(numbers, index, i)
        console.log(`swap ${index}: ${i}`)
        console.log(numbers)
    }
}
  return numbers
}

快速排序

通俗的说就是以某某为基准,小的去前面,大的去后面
重复这段话,就能排序

let quickSort = arr => {
    if (arr.length <= 1) { return arr }
    let pivotIndex = Math.floor(arr.length / 2)  //Math.floor向下取整
    let pivot = arr.splice(pivotIndex, 1)[0]  //为什么要写[0],如果不写返回的是被切掉的数组,所以提取数组中的第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)
    )
}

归并排序

例如有一个数组[12, 3, 7, 21, 5, 9, 4, 6]
分成左边一半排好序,右边一半排好序
然后把左右两边合并(merge)起来
就变成了[3, 7, 12, 21]和[4, 5, 6, 9]
再分别比较两个数组的第0项,最小的提出来排前面,再把剩余的数组合并继续比较第0项

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))
}

计数排序

用一个哈希表作记录
发现数字 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
}

其他的排序算法

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Get Started • 选择排序○ 遍历○ 递归• 快速排序○ 每次取中间项,小左右大• 归并排序○ 左右两边...
    茜Akane阅读 221评论 0 0
  • 排序动图链接,自行查找。https://visualgo.net/zh/sorting 快速排序 快速排序可能是应...
    无业大学生阅读 412评论 0 0
  • 什么是算法 高德纳在《计算机程序设计艺术》里对算法的归纳:书籍推荐:《数据结构与算法分析》 输入:一个算法必须有零...
    Tuuu阅读 413评论 0 0
  • 十大经典算法排序总结对比一张图概括,主流排序算法概览: 名词解释:n: 数据规模k:“桶”的个数In-place:...
    飞菲fly阅读 663评论 0 2
  • 插入排序 借用《算法导论》里的例子,就是我们打牌的时候,每新拿一张牌都会把它按顺序插入,这,其实就是插入排序。 齐...
    码农田小齐阅读 195评论 0 0