快速排序算法的性能比较

一、前言

  最近又遇到快速排序算法了,才发现以前学的那种快速排序算法有问题,在此记录一下两种不同快速排序算法的性能比较

二、快速排序算法

2.1 方法一

思路:

  ①选择数组中间数作为基数,并从数组中取出此基数

  ②准备两个新数组容器,遍历数组,逐个与基数比对,较小的放左边容器,较大的放右边容器

  ③递归处理两个容器的元素,并将处理后的数据与基数按大小合并成一个数组,返回

实现:

// 方法一
function quick_1(ary) {
    //4.结束递归(当ARY中小于等于一项,则不用处理)
    if (ary.length <= 1) {
        return ary
    }
    // 1.找到数组的中间项,在原有的数组中把它移除
    let middleIndex = Math.floor(ary.length / 2)
    let middleValue = ary.splice(middleIndex, 1)[0]
    //2.准备左右两个数组,循环剩下数组中的每一项,比当前项小的放到左边数组中,反之放到右边数组当中
    let aryLeft = [],
        aryRight = []
    for (let i = 0; i < ary.length; i++) {
        ary[i] < middleValue ? aryLeft.push(ary[i]) : aryRight.push(ary[i])
    }
    //3.递归方式让左右两边的数组持续这样处理,一直到左右两边都排好序为止(最后让左边+中间+右边拼接成为最后的结果)
    return quick_1(aryLeft).concat(middleValue, quick_1(aryRight))
} 

缺点:

  ①调用函数删除基数会更耗时

  ②专门生成两个数组来存储,增加了空间复杂度

2.2 方法二

思路:

  ①选定pivot中心轴

  ②设定两个下标L和R,不断从右向左移动R下标,一旦发现比pivot小的数字,则把该数字放到L所指的位置;然后不断向右移动L下标,一旦发现比pivot大的数字,则把该数字放到R所指的位置;然后从右向左移动R下标,重复执行之前的步骤,直到L等于R,结束第一次排序

  ③分别对左右子序列重复前两步操作

实现:

// 方法二
function quick_2(arry) {
    return quick(arry, 0, arry.length - 1)
}

function quick(arry, L, R) {
    if (L >= R) return
    let left = L
    let right = R
    let pivot = arry[left]
    while (left < right) {
        while (left < right && arry[right] >= pivot) {
            right--
        }
        if (left < right) {
            arry[left] = arry[right]
        }
        while (left < right && arry[left] <= pivot) {
            left++
        }
        if (left < right) {
            arry[right] = arry[left]
        }
        if (left >= right) {
            arry[left] = pivot
        }
    }
    quick(arry, L, right - 1)
    quick(arry, right + 1, R)
}

优点:

  ①利用双指针移动下标取数,更快

  ②不需要创建新数组存储被划分的数据,降低了空间复杂度

三、性能比较

测试:

  选取100000个随机整数,利用两种不同思路的快速排序方法进行性能测试

// 方法一
function quick_1(ary) {
    //4.结束递归(当ARY中小于等于一项,则不用处理)
    if (ary.length <= 1) {
        return ary
    }
    // 1.找到数组的中间项,在原有的数组中把它移除
    let middleIndex = Math.floor(ary.length / 2)
    let middleValue = ary.splice(middleIndex, 1)[0]
    //2.准备左右两个数组,循环剩下数组中的每一项,比当前项小的放到左边数组中,反之放到右边数组当中
    let aryLeft = [],
        aryRight = []
    for (let i = 0; i < ary.length; i++) {
        ary[i] < middleValue ? aryLeft.push(ary[i]) : aryRight.push(ary[i])
    }
    //3.递归方式让左右两边的数组持续这样处理,一直到左右两边都排好序为止(最后让左边+中间+右边拼接成为最后的结果)
    return quick_1(aryLeft).concat(middleValue, quick_1(aryRight))
}


// 方法二
function quick_2(arry) {
    return quick(arry, 0, arry.length - 1)
}

function quick(arry, L, R) {
    if (L >= R) return
    let left = L
    let right = R
    let pivot = arry[left]
    while (left < right) {
        while (left < right && arry[right] >= pivot) {
            right--
        }
        if (left < right) {
            arry[left] = arry[right]
        }
        while (left < right && arry[left] <= pivot) {
            left++
        }
        if (left < right) {
            arry[right] = arry[left]
        }
        if (left >= right) {
            arry[left] = pivot
        }
    }
    quick(arry, L, right - 1)
    quick(arry, right + 1, R)
}

// 生成num个m到n范围内的随机整数
function getRandom(m, n, num) {
    let list = []
    for (let i = 0; i < num; i++) {
        let random = Math.floor(Math.random() * (n - m) + m)
        list.push(random)
    }
    return list
}

let ary = getRandom(1, 100000, 100000)
let ary1 = [...ary]
let ary2 = [...ary]

console.time('quick_1')
quick_1(ary1)
console.timeEnd('quick_1')

console.time('quick_2')
quick_2(ary2)
console.timeEnd('quick_2')

测试结果:(在谷歌浏览器上测试)

结论:

方法二实现排序更快速更省空间

四、参考

https://www.cnblogs.com/hjx-blog/articles/9183453.html

https://www.bilibili.com/video/BV1at411T75o?from=search&seid=11059659226640382451

https://www.bilibili.com/video/BV1YE411a7zK

 

原文地址:https://www.cnblogs.com/FHC1994/p/13029765.html