快速排序
快速排序(QuickSort)是对冒泡排序(BubbleSort)的一种改进。
快速排序由C. A. R. Hoare在1960年提出,这是这位图灵奖得主在很年轻的时候想出来的,XMSL。
它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
其基本的编程实现,我已经在这篇文章中展示过了,各种排序算法都有——《基础排序算法大全(Java语言描述)》。
快排的效率很高,对随机序列是很好使的,但也可能遇到O(N2)的糟糕情况。不过只要加以优化这种情况基本不可能出现。
快速排序是数据结构与算法的重要知识,是程序员和计算机专业学生必知必会的重要算法,是面试的常考重点,其性能优化也是需要思考的问题……
总之,一定要会!很会很会!
快速排序的流程
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
- 首先设定一个分界值,通过该分界值将数组分成左右两部分。
- 将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
- 然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
- 重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
动图演示
优化策略
其实快排是一个天才般的算法,对其优化主要从两个角度展开:
一是使得选取的中轴更合理,尽量使得划分均匀一些。
二是试图在快排效率不好的时候更换算法或与其他算法组合,使之更高效。
下面给一些说明。
1.二分
运用二分思想,每次取划分出的区间的中值作为新的一次划分的中轴。
一般来说,快排对随机序列很好使,所以说我们可以认为,平均来讲去取中值更可能划分的比较均匀。
二分取中值可能优于取一端的值(事实上确实如此,比如洛谷P1177这个题,哪怕你用C++,你写普通版快排还是会TLE掉3个点)……
2.随机化
既然快排本身面对的就是随机序列,其实我们可以认为随机化地取枢纽值会比任意取好一些,至少比直接取一侧的值好一些。
随机化本身就可能是一种算法优化的策略,至少也是一种参考指标。
3.与其他排序结合
虽说快排确实是快,但有些情况下,比如遇到不利情况会变成O(N2),比如说在分治到小区间时甚至可能不如简单的排序算法……
所以说可以与其他排序算法结合,在合适的时候运用合适的算法进行高效协作。
而这部分可以参考我在文末附的一位大佬的博文。
想体会到这种策略是多么强,可以参看伟大的TimSort~
C++编程实现
这个版本是利用了二分的思想对算法进行了优化,使得原先的算法性能得到了一定程度的提高。
#include<iostream>
using namespace std;
int num, array[100001];
/**
* 应用二分思想
* @param left 左索引
* @param right 右索引
*/
void quickSort(int left, int right) {
int mid = array[(left+right)/2];//中间数
int i = left, j = right;
do {
while (array[i] < mid) {
i++;//查找左半部分比中间数大的数
}
while (array[j] > mid) {
j--;//查找右半部分比中间数小的数
}
if (i <= j) {
swap(array[i], array[j]);//交换
i++;
j--;
}
} while (i <= j);
if (left < j) {
quickSort(left, j);//递归搜索左半部分
}
if (i < right) {
quickSort(i, right);//递归搜索右半部分
}
}
int main() {
cin >> num;
for (int i = 1; i <= num; i++) {
cin >> array[i];
}
quickSort(1, num);
for (int i = 1; i <= num; i++) {
cout << array[i] << " ";
}
}
另一个版本
谈谈快速排序与二路归并排序的思想
有关于这二者的时空复杂度啊、稳定性啊这些问题我就不想赘述了,我的博客也有——《排序综述》,很详细。
这里说的是二者的思想。
我在这篇文章——《算法分析与设计中的几种思想/策略》中也提到了,归并排序和快速排序同样都使用了分治的思想,可以通过递归很好的完成排序工作。
但快速排序的核心算法是划分,二路归并排序的核心算法是合并。
大家有没有想过这样一件事:快速排序和二路归并排序分别是在什么时候完成的排序?
其实是这样的:
快速排序在划分的时候,将大的放到轴右侧、小的换到轴的左侧,在这个划分的时候完成的排序啊,它的“分治”,“分”是核心,所以“划分”是核心算法,等到分无可分就完成了子区间的排序,子区间都排完,整体区间就完成了排序。
而二路归并排序的“分”只是为了二分地分解大区间为小的子区间,它是在“合”的时候完成的排序啊,它的“分治”,“并”是核心,所以“合并”是核心算法,等到分无可分就可以逐步合并区间了,最终就完成了整体区间的排序。
总结:
快速排序在划分的时候完成排序,
重点在“分”;
归并排序在“并”的时候完成排序,
重点在“并”。
此外,快速排序属于交换排序,与归并排序有很大的不同,快排的“分”也是通过交换完成的。
就说这么多……
总结
看了这么多,你掌握快速排序了吗?看在我这么辛苦的份上,是不是应该用一个一个赞向我砸来呢?
OrzOrzOrz……
来源:CSDN
作者:进阶的JFarmer
链接:https://blog.csdn.net/weixin_43896318/article/details/104256706