快速排序详解及应用

快速排序详解及应用

什么是快速排序?

1.快速排序明明就是一个数组算法,和二叉树有什么关系?

首先我们要知道的是所有递归的算法,你甭管它是干什么的,本质上都是在遍历一棵(递归)树,然后在节点(前中后序位置)上执行代码,你要写递归算法,本质上就是要告诉每个节点需要做什么。

然后看看归并排序的代码框架:

void sort(int[] nums, int low, int high) {
   
    if (low >= high) {
   
        return;
    }
    // 对 nums[low..high] 进行切分
    // 使得 nums[low..p-1] <= nums[p] < nums[p+1..high]
    int p = partition(nums, low, hi);
    // 去左右子数组进行切分
    sort(nums, low, p - 1);
    sort(nums, p + 1, high);
}

显然快速排序就是先对整个数组进行切分,再分别对左右子数组进行切分。也就是快速排序是先将一个元素排好序然后再将剩下的元素排好序。

快速排序的核心是partition函数,partition函数的作用是在nums[low…high]中寻找一个分界点p,通过交换元素使得nums[low…p-10]都小于等于nums[p],且nums[p+1…high]都大于nums[p]

在这里插入图片描述

一个元素左边的元素都比它小,右边的元素都比它大,是什么意思?

也就是说在经过一轮的partition之后,就会让nums[p]放在正确的位置上。(即nums[p]已经被排好序了)

那接下来就是将剩下的元素排好序

那剩下的元素有哪些?很明显,元素都分布在左边和右边,所以我们可以对子数组进行递归,用partition函数把剩下的元素也排好序

但这里我们可以想到二叉树的前序遍历

/* 二叉树遍历框架 */
void traverse(TreeNode root) {
   
    if (root == null) {
   
        return;
    }
    /****** 前序位置 ******/
    print(root.val);
    /*********************/
    traverse(root.left);
    traverse(root.right);
}

因此得出结论:快速排序的过程可以抽象成一棵二叉树,把子数组 nums[lo..hi] 理解成二叉树节点上的值,sort 函数理解成二叉树的遍历函数

参照二叉树的前序遍历顺序,快速排序可以如图所示,第二个数组原本是空的,在经过多次partition之后按颜色顺序被填入到数组中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nUVlk44p-1680504623264)(D:\Development\Typora\img\image-20230403094217056.png)]

可以注意到最后形成的这棵二叉树是一棵二叉搜索树。

为什么最后得到的是一棵二叉搜索树呢?是巧合吗?

不是巧合,这是因为 partition 函数每次都将数组切分成左小右大两部分,恰好和二叉搜索树左小右大的特性吻合。

因此我们也可以把快速排序的过程理解成是一个构造二叉搜索树的过程

但是谈到二叉搜索树的构造,那就不得不说二叉搜索树不平衡的极端情况,极端情况下二叉搜索树会退化成一个链表,导致操作效率大幅降低。

快速排序的过程中也有类似的情况,比如我画的图中每次 partition 函数选出的分界点都能把 nums[low..high] 平分成两半,但现实中你不见得运气这么好。

如果你每次运气都特别背,有一边的元素特别少的话,这样会导致二叉树生长不平衡:

在这里插入图片描述

这样的话,时间复杂度会大幅上升

解决办法:

我们为了避免出现这种极端情况,需要引入随机性

常见的方式是在进行排序之前对整个数组执行 洗牌算法进行打乱,或者在 partition函数中随机选择数组元素作为分界点,本文会使用前者。

2.快速排序代码实现

class Quick {
   

    public static void sort(int[] nums) {
   
        // 为了避免出现耗时的极端情况,先随机打乱
        shuffle(nums);
        // 排序整个数组(原地修改)
        sort(nums, 0, nums.length - 1);
    }

    private static void 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值