[leetcode: 912. 排序数组 (快排思想)] | 刷题打卡

531 阅读1分钟

快速排序

算法介绍

快速排序是由东尼·霍尔所发展的一种排序算法。其基本思想是,通过一趟排序将待排区间分隔成独立的两部分,然后分别对这两部分继续进行排序,以达到整个序列有序。

算法过程

  1. 找到标兵

  2. 将当前区间根据标兵分成俩部分

  3. 对俩个区间分别进行排序

代码实现

function quick_sort(l, r, nums) {
    // 边界判断, 如果当前只剩下一个元素, 那么直接返回.
    if(l >= r) return ;
    // 1. 找到标兵
    let x = nums[l + r >> 1], // l + r >> 1相当于Math.floor((l + r) / 2) 这样写方便一点
        i = l - 1, // 让i指向左端点的前一位
        j = r + 1; // 让j指向右端点的后一位
    
    // 2. 根据标兵将数组拆分成俩部分
    while(i < j) {
        do i ++; while(nums[i] < x); // 从左边往右走, 找到第一个不小于x的数字
        do j --; while(nums[j] > x); // 从右边往左走, 找到第一个不大于x的数字
        if(i < j) [nums[i], nums[j]] = [nums[j], nums[i]]; // 若此时i < j, 交换俩数字
    }

    // 3. 递归处理剩余的俩个部分
    quick_sort(l, j), quick_sort(j + 1, r);
}

代码讲解

  1. 为什么要让i指针指向左端点的前一位, j指针指向右端点的后一位

主要是为了让第二步将数组分成俩部分的代码更好实现, 使用do, while的结构会先让i++, 以及j--.

  1. 为什么俩个区间分别是l ~ j和j + 1 ~ r

i指针的作用是找到不小于x的数, j指针的作用是找到不大于x的数, 然后交换i和j. 最后的结果就是, j右边都是大于x的, j包括j前面的数都是小于等于x的. 所以可以根据j这个点作为分割点, 将数组分成俩部分.

复杂度分析

时间复杂度

最优时是O(NlogN)的, 最差时是O(N^2)的

  1. 最优: 每次都可以将数组平均分成俩半, 对于一个长度为N的数组, 需要进行logN次操作让子区间变成1, 也就是说一共会递归logN层. 在每一层我们都需要指定俩个指针扫描一遍数组的每个元素, 所以每一层需要计算N次. 层数 * 每层计算的次数 = 整个算法的时间复杂度 = O(NlogN)

  2. 最差: 每次选择标兵时都选到了一个最大值或最小值, 那么数组最后会被分成一个数和(N - 1)个数, 最后一共需要递归N层. 在每一层都需要遍历一遍数组, 需要N次计算. 所以整个算法的时间复杂度是O(N^2)的

空间复杂度

O(1)

只是创建了一些临时的变量, 标兵, 俩个指针. 所以是O(1)的

原题链接

leetcode: 912. 排序数组

AC代码

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
    quick_sort(0, nums.length - 1);
    return nums;
    function quick_sort(l, r) {
        if(l >= r) return ;
        let x = nums[l + r >> 1],
            i = l - 1,
            j = r + 1;
        while(i < j) {
            do i ++; while(nums[i] < x);
            do j --; while(nums[j] > x);
            if(i < j) [nums[i], nums[j]] = [nums[j], nums[i]];
        }
        quick_sort(l, j), quick_sort(j + 1, r);
    }
};

扩展题

有兴趣的同学可以做一下这道题目,这道题目也是基于快排的思想,用的是快速查找算法。

leetcode: 215. 数组中的第K个最大元素

AC代码

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number}
 */
var findKthLargest = function(nums, k) {
    return quick_sort(0, nums.length - 1, nums.length - k + 1);
    function quick_sort(l, r, k) {
        if(l >= r) return nums[l];
        let i = l - 1, j = r + 1, x = nums[l + r >> 1];
        while(i < j) {
            do i ++; while(nums[i] < x);
            do j --; while(nums[j] > x);
            if(i < j) [nums[i], nums[j]] = [nums[j], nums[i]];
        }
        let len = j - l + 1;
        if(k <= len) return quick_sort(l, j, k);
        return quick_sort(j + 1, r, k - len);
    }
};

本文正在参与「掘金 3 月闯关活动」,点击查看活动详情