《排序算法 一 1》

75 阅读1分钟

选择排序

我们以一段代码为例:

请写出一个 sort 函数,要求 sort(numbers) 能返回一个把 numbers 从小到大排列的数组(可以添加多余的帮助函数,且不用 JS 内置的 sort 函数)

  • 先用递归写
let min = (numbers) => { // 找到最小数
    return min(
        [numbers[0],min(numbers.slice(1))]
    )
}

let minIndex = (numbers) => { // 找到最小数的下标
    return numbers.indexOf(min(numbers))
}

let sort = (numbers) => { 
    let index = minIndex(numbers)
    let min = numbers[index]
    if(numbers.length > 2){
        numbers.splice(index,1) 
        return [min].concat(sort(numbers))
    }else{
        return numbers[0] < numbers[1] ? numbers : numbers.reverse()
    }
}

但是上面的代码看着就很繁琐,于是我们改写下代码

  • 用循环来写
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 sort = (numbers) => {
    for(let i = 0; i < numbers.length -1; i++){
        let index = minIndex(numbers.slice(i) + i)
        swap(numbers, index, i)
    }
}

这样看起来就会好一些,更好懂些,这就是选择排序,每次都选择最小/大的,选完就结束排序了。

快速排序

我们用递归的思路

以某某为基准

  • 想象你是一个体育委员
  • 你面对的同学为 [12, 3, 7, 21, 5, 9, 4, 6]
  • 以某某为基准一般都取中间那个,小的去前面,大的去后面
  • 你只需要重复说这句话,就能排序

用图说明一下

6b42498eb68b024f59015cf9bb959a9.jpg

代码如下

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

归并排序

我们用递归的思路

不以某某某为基准

  • 想象你是一个体育委员
  • 你面对的同学为 [12, 3, 7, 21, 5, 9, 4, 6]
  • 左边一半排好序,右边一半排好序
  • 然后把左右两边合并( merge )起来

代码如下

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 contSort = (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 hsahTable){
            for(let k = 0; k < hashTable[j]; k++){
                result.push(j)
            }
        }
    }
    return result
}

时间复杂度

选择排序 O(n^2)

快速排序 O(n log2 n)

归并排序 O(n log2 n)

计数排序 O(n + max)