快速排序

发布时间 2023-07-19 21:40:14作者: typerxiaozhu

快速排序

主要思想

快速排序所采用的思想是分治的思想。所谓分治,就是指以一个数为基准,将序列中的其他数往它两边“扔”。以从小到大排序为例,比它小的都“扔”到它的左边,比它大的都“扔”到它的右边,然后左右两边再分别重复这个操作,不停地分,直至分到每一个分区的基准数的左边或者右边都只剩一个数为止。这时排序也就完成了。

流程

  • 确定基准值\(x\),选择的方式一般有三种:左端点 右端点 中间点

    基准值的选择不同也会导致代码的运行时长不同,一般来说都取中间点

  • 调整区间,使左半边全部 \(\leq x\),右半边全部 \(\geq x\)

  • 按照以上两个步骤递归处理两端,直到区间里只剩下一个元素

具体实现

朴素做法

  • 开两个数组,\(a\)和\(b\),再开一个数组\(q\)用来存储答案
  • 定义一个函数用来处理数组,需要两个参数\(l\)和\(r\),表示左端点和右端点,从\(l\)遍历到\(r\)
  • 分情况处理:
    • \(q_i\leq x\) 将\(q_i\)放到\(a\)数组中
    • \(q_i\geq x\) 将\(q_i\)放到\(b\)数组中
  • 重复2 3 两步操作,直到区间里只剩下一个元素,即 \(l\geq r\)
  • 最后合并两个数组

优化思路

按照以上方法,我们将会浪费很多空间,那么如何在不另外开辟空间的情况下完成任务呢?

我们可以借鉴双指针的思路,为了好听我们将两个指针命名为 “哨兵”

我们先来回顾一下,双指针的主要用途是什么? 主要是用来维护一段区间

于是我们可以用 \(i\) 来维护左区间,使得 \(i\) 左边的数都 \(\leq x\),用 \(j\) 来维护右区间,使得 \(j\) 右边的数都 \(\geq x\)

优化做法

  • 定义两个“哨兵” \(i\) 和 \(j\),开始时指向区间的左右两端
  • 当 \(i\) 指向的数 \(<x\) 时,就往右移动,直到指向的数 \(\geq x\)
  • 当 \(j\) 指向的数 \(> x\) 时,就往左移动,直到指向的数 \(\leq x\)
  • 当 \(i\) 和 \(j\) 的移动停止时,交换 \(i\) 和 \(j\) 指向的数,\(i\) 往右移动一个,\(j\) 往左移动一个

一直重复以上的步骤,直到两个哨兵相遇为止,即当 \(i\geq j\) 时

样例模拟

注意点

快排与二分的注意点是一样的,都是边界问题

在递归分别处理时容易出现这个问题

  • 当分界点取 \(i-1\) 和 \(i\) 时,分界点不能取 \(l\),要取 \((l+r+1)/2\),注意上取整
  • 当分界点取 \(j\) 和 \(j+1\) 时,分界点不能取 \(r\),要取 \((l+r)/2\),区别于上一种