一、快速排序算法(Quick Sort)简介

快速排序(Quick sort),使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。它是一种 分而治之思想 在排序算法上的典型应用。本质上来看,快速排序是对冒泡排序算法的一种改进,是在冒泡排序基础上的 递归分治法

快速排序算法的优点在于 快,而且效率高 !它是处理大数据最快的排序算法之一了。


二、 算法基本思想和流程(期望时间复杂度 QFileSystemModel排序 q排序法_排序算法 ,最坏情况 QFileSystemModel排序 q排序法_学习_02

QFileSystemModel排序 q排序法_QFileSystemModel排序_03

  • Step1——确定分界点x(三种常用的方法):q[l]q[(l+r)/2]q[r],或者随机确定。
  • Step2——将待排序列调整成两个区间,使得满足以下两个原则(具体方法后面介绍):
  • 原则一:第一个区间里的所有数都 ≤x
  • 原则二:第二个区间里的所有数都 ≥x

注意:要注意此处写代码的逻辑。并不是把所有小于等于x的数放在左边,所有大于等于x的数放在右边。 因为等于x的数可能在左边也可能在右边。

  • Step3——递归处理左、右两端,将左边和右边都排好序,Finish!

2. 难点/关键点:步骤二的区间调整方法

(1)暴力做法: 用两个额外的空间(增加了空间复杂度,但时间复杂度不变:O(n))

  • 定义两个额外的数组 a[]b[] ,其中数组 a[] 用来存放 ≤x 的所有数、数组 b[] 用来存放 >x 的所有数。
  • 方法:遍历序列q,再写一个判断if-else分支语句即可。
  • 然后,将 a 放前面, b 放后面组成的新数组 q* 即实现了区间调整。

(2)优美做法: 无需开辟额外空间(时间复杂度:O(n))

  • 定义两个指针:i = l 以及 j = r,即指针的初始值一个在最左边,一个在最右边。
  • 两指针分别往中间移动,并进行以下判断:
  • 如果 q[i] < x ,则满足原则一,i++,直到某个数 q[i] ≥ x ,停下来,开始移动 j
  • 如果 q[j] > x ,则满足原则二,j-- ,直到某个数 q[j] ≤ x ,停下来;
  • 交换两个指针指向的数: swap(q[i], q[j])
  • 返回第一步,继续移动 ij ,直到相遇为止。

三. 快排模板(背诵)

模板题:AcWing 785. 快速排序

注意:此题数据较大且多,输入输出建议使用 scanfprintf ,要使用C++的 cincout 需要进行速度优化,参考另一篇博文:C++ 输入输出(cin & cout)加速/效率优化 。

模板一

void quick_sort(int q[], int l, int r)
{
    // 1.判边界
    if (l >= r) return;
    
	// 2.区间调整:双指针法,指针边界往外扩了一格偏移量
	// 原因是每次判断前无论是否满足两个原则,两指针都会提前往中间移动一格
	// 注意:x 为下取整,递归时的边界是 j 和 j + 1.
    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
	
	//3.递归处理左、右两端
    quick_sort(q, l, j);
    quick_sort(q, j + 1, r);
}

模板二

void quick_sort(int q[], int l, int r)
{
    // 1.判边界
    if (l >= r) return;
    
	// 2.区间调整:双指针法,指针边界往外扩了一格偏移量
	// 原因是每次判断前无论是否满足两个原则,两指针都会提前往中间移动一格
	// 注意:x 为上取整,递归时的边界是 i - 1 和 i.
    int i = l - 1, j = r + 1, x = q[l + r + 1 >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
	
	//3.递归处理左、右两端
    quick_sort(q, l, i - 1);
    quick_sort(q, i, r);
}

【分析避免边界问题】

  • 当递归边界取的 i,即 quick_sort(q, l, i - 1); quick_sort(q, i, r);,则为了避免陷入死循环:
  • 边界点 x 一定不能等于 q[l],且如果取中间值,需要上取整 x = q[l + r + 1 >> 1]
  • 当递归边界取的 j,即 quick_sort(q, l, j); quick_sort(q, j + 1, r);,则为了避免陷入死循环:
  • 边界点 x 一定不能等于 q[r],且如果取中间值,需要下取整 x = q[l + r >> 1]