二十天刷题计划-- 快速排序

141 阅读2分钟

「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战」。

1.题目

排序数组

给你一个整数数组 nums,请你将该数组升序排列。

 

示例 1:

输入:nums = [5,2,3,1]

输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]

输出:[0,0,1,1,2,5]  

提示:

1 <= nums.length <= 5 * 104

-5 * 104 <= nums[i] <= 5 * 104


思路

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两个子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有元素都排在相应位置上为止。

基本思路:快速排序每一次都排定一个元素(这个元素呆在了它最终应该呆的位置),然后递归地去排它左边的部分和右边的部分,依次进行下去,直到数组有序;

算法思想:分而治之(分治思想),与「归并排序」不同,「快速排序」在「分」这件事情上不想「归并排序」无脑地一分为二,而是采用了 partition 的方法(书上,和网上都有介绍,就不展开了),因此就没有「合」的过程。

实现细节(注意事项):(针对特殊测试用例:顺序数组或者逆序数组)一定要随机化选择切分元素(pivot),否则在输入数组是有序数组或者是逆序数组的时候,快速排序会变得非常慢(等同于冒泡排序或者「选择排序」);

时间复杂度:基于随机选取主元的快速排序时间复杂度为期望 O(n\log n)O(nlogn),其中 nn 为数组的长度。

空间复杂度:O(h)O(h),其中 hh 为快速排序递归调用的层数。我们需要额外的 O(h)O(h) 的递归调用的栈空间,由于划分的结果不同导致了快速排序递归调用的层数也会不同,最坏情况下需 O(n)O(n) 的空间,最优情况下每次都平衡,此时整个递归树高度为 \log nlogn,空间复杂度为 O(\log n)O(logn)。


代码

var sortArray = function(nums) {
    // 单独开辟两个存储空间leftright来存储每次递归比target小和大的序列
    // 每次递归直接返回left、target、right拼接后的数组
    if (nums.length < 2) return nums
    const target = nums[0]
    const left = []
    const right = []
    for (let i = 1;i<nums.length;i++) {
        if (nums[i] < target) {
            left.push(nums[i])
        } else {
            right.push(nums[i])
        }
    }
    return sortArray(left).concat([target], sortArray(right))
};