四种常用的排序算法

244 阅读1分钟

快速排序

  • 快速排序是公认速度最快的算法之一。

  • 快速排序的基本思路:

    1. 确定一个支点(pivot),通常选择数组的中间值
    2. 将所有小于支点的值放在左侧(升序,反之则降序),将所有大于支点的值放在右侧
    3. 重复以上过程,直到所有排序完成
  • 具体做法(升序为例):

    1. 确定支点(pivot):用数组的长度除以 2 ,并向下取整,获取中间值,得到支点的索引,通过索引获取支点的值。
    2. 准备用于存放小于和大于支点的元素的空数组,比如left和right
    3. 获得不包含支点的数组,建立指针,指向新数组第一个元素
    4. 指针的当前值与支点比较,小于支点则放入left,大于则放入right,指针指向下一个当前值
    5. 对left和right重复以上操作
    6. 依次连接三个数组(left、支点、right)并返回,排序完成
  • JS实现(递归实现):

        let quickSort = array => {
            if(array.length < 2) {
                return array
            } else {
                let left = []
                let right = []
                let index = Math.floor(array.length / 2)
                /* splice函数的三个注意点:
                    1. 函数的返回值是被截取的值
                    2. 函数的返回值总是一个数组
                    3. 函数的执行会影响原数组  */
                let pivot = array.splice(index, 1)[0]
                for (let i = 0; i < array.length; i++) {
                    array[i] < pivot ? left.push(array[i]) : right.push(array[i])
                }
                return quickSort(left).concat([pivot], quickSort(right))
            }
        }
    

    选择排序

  • 选择排序的基本思路:

    • (升序)每次找出数组内的最小值,放在数组最前面
  • 具体做法(升序):

    1. 定义获取最小值索引的函数
    2. 通过最小值索引函数获得最小值和最小值的索引号,通过索引号截取不包含最小值的数组
    3. 重复以上,获得所有最小值数组([最小值] + [其他])
    4. 依次连接所有数组(concat),排序完成
  • JS实现(递归实现)

    let minIndex = array => {
        let index = 0;
        if (array.length < 2) { return index }
        for (let i = 0; i < array.length; i++) {
            index = array[index] < array[i] ? index : i
        }
        return index
    }
    let chooseSort = array => {
        if (array.length < 2) { return array }
        let index = minIndex(array)
        let min = array[index]
        // 注意splice的返回值
        array.splice(index, 1)
        return [min].concat(chooseSort(array))
    }

归并排序

  • 也叫做合并排序,效率较高,是一种广泛使用的排序算法

  • 基本思路:

    • 将两个已经排序的数组进行两两合并,这要比从头开始排序来的快。将数组拆分,分成n个只有一个元素的数组,然后不断地两两合并,直到全部排序完成。
  • 具体做法(升序):

    1. 将数组切片,左边为从开始到中间,右边为从中间到结束,不断重复,直到每个数组只有一个值
    2. 设定指针,分别指向两端当前第一个值。
    3. 对指针两端两两比较,将符合条件的值取出,放在前面,不断重复
    4. 合并比较完的数组
  • JS实现(递归实现)

    let merge = (left, right) => {
        if (left.length === 0) { return right }
        if (right.length === 0) { return left }
        return left[0] < right[0] ? [left[0]].concat(merge(left.slice(1), right)) : [right[0]].concat(merge(left, right.slice(1)))
    }
    let mergeSort = array => {
        if (array.length < 2) { return array }
        let left = array.splice(0, array.length / 2)
        let right = array.splice(array.lengt / 2)
        return merge(mergeSort(left), mergeSort(right))
    }

计数排序

  • 基本思路:

    • 使用哈希表,将每一个数组元素以 key: value 的形式置入哈希表,并找出key的最大值 max,key 为数组元素,value 为出现的次数,以 max 为最大次数遍历哈希表,遍历过程中将在哈希表中出现的 max 的值置入数组,完成排序。计数排序比以上排序都要快,因为它只遍历数组一遍。但需要额外使用哈希表,是典型的用空间换时间。
  • 具体做法:

    1. 定义哈希表 hash、最大值 max,初始值为 0、和用于完成排序的空数组。
    2. 遍历数组,将每个元素作为 key,元素出现的次数作为 value,置入哈希表,同时找出最大值 max
    3. 以 max 的值为最大次数遍历哈希表,如果 max 的当前值,等于哈希表的 key,将 max 的当前值,置入数组 value 次
    4. 完成排序,返回排序完成的数组
  • JS实现

    let countSort = array => {
        let hash = {}
        let max = 0
        let result = []
        for (let i = 0; i < array.length; i++) {
            if(!hash[array[i]]) {
                hash[array[i]] = 1
            } else {
                hash[array[i]]++
            }
            max = max > array[i] ? max : array[i]
        }
        // max的值在循环中是可以取到的
        for (let i = 0; i <= max; i++) {
            if (i in hash) {
                for (let j = 0; j < hash[i]; j++) {
                // 循环中的 i 的值,就是 hash 的 key
                    result.push(i)
                }
            }
        }
        return result
    }