排序算法:快速排序
Sorting Algorithms:Quick Sort
前言
该博客用于本弱鸡复习巩固,打牢基础,还望各大佬不吝赐教。
基本思路
1.对一个未排序序列,假设从该序列中的元素中取一个基准值pivotkey,将小于pivotkey放左边,大于pivotkey放右边; 2.接着以该k为中间,左右两边的分割作为新的序列,重新进行1操作。 快排因为用到了递归操作,所以在简单排序中性能不如直接插入排序, 而在大量数据排序时,递归产生的性能影响对于算法的整体性能优势可以忽略。
动图示例

算法复杂度分析
平均 | 最坏 | 最好 | 稳定性 | 空间复杂度 |
---|---|---|---|---|
O(nlogn) | O(n^2) | O(nlogn) | 不稳定 | O(logn) |
p.s.
- 最坏情况:待排序为正序或逆序,这样每次分割后的子序列一个之比上一次序列少一个元素,一个为空。如 1 2 3 4 5 pivotkey=1;分割后一个序列为 2 3 4 5 一个为空,最终O(n^2)
- 最好情况:每一次分割都能平分,很均匀 O(nlogn)
- 平均情况:O(n*logn) 数学归纳法
- 空间复杂度:主要由递归而产生的对栈空间的影响
- 最好:O(logn)
- 最坏:O(n)
- 平均:O(logn)
- 稳定性 不稳定 比较和交换是跳跃进行的
代码实现
import java.util.Arrays;import java.util.Random;/** * QuickSort * 1.对一个未排序序列,假设从该序列中的元素中取一个基准值pivotkey,将<pivotkey放左边 >pivotkey放右边 * 2.接着以该k为中间,左右两边的分割作为新的序列,重新进行1操作 * <p> * 快排因为用到了递归操作,所以在简单排序中性能不如直接插入排序 * 而在大量数据排序时,递归产生的性能影响对于算法的整体性能优势可以忽略 * <p> * 时间复杂度分析: * 最坏情况:待排序为正序或逆序,这样每次分割后的子序列一个之比上一次序列少一个元素,一个为空 * 如 1 2 3 4 5 pivotkey=1;分割后一个序列为 2 3 4 5 一个为空 * 最终O(n^2) * 最好情况:每一次分割都能平分,很均匀 O(n*logn) * 平均情况:O(n*logn) 数学归纳法 * 空间复杂度:主要有地柜而产生的对栈空间的影响 * 最好:O(logn) * 最坏:O(n) * 平均:O(logn) * 稳定性 不稳定 比较和交换是跳跃进行的 * <p> * 如何合理基准值pivotkey * 该值的取值对该算法有相当影响,若pivotkey取到了最大或最小,都会增加算法复杂度,影响性能 * 1.随机选取,在待排序列中随机选取,以降低取到最大或最小值的概率 * 2.三数取中,在待排序列的左端,中间,右端去三个值选取中位数,节省随机数产生的时间开销,以降低取到最大或最小值的概率 * 三数取中时,比较的同时应将三个元素按中间,小,大的顺序重新排好位置 * 3.九数取中,三次取样,每次取三个数,取它们的中位数,再取三个中位数的中位数 */public class QuickSort { public static void main(String[] args) { int[] a = new int[10]; boolean flag = true; //random array for (int i = 0; i < a.length; i++) { Random rd = new Random(); a[i] = rd.nextInt(10); } System.out.println("Random Array :"); System.out.println(Arrays.toString(a)); System.out.println(); System.out.println("Quick Sort :"); //快速排序开始 quickSort(a, 0, a.length - 1); System.out.println(Arrays.toString(a)); } /** * @param a * @param low * @param high */ public static void quickSort(int[] a, int low, int high) { //该值定义了从哪个位置开始分割序列 int pivot; //当high-low大于某一值时适合快速排序 //if ((high - low) >MAX_LENGTH_INSERT_SORT) 该值取7或50 if (low < high) { //partition方法对序列进行排序 pivot = partition(a, low, high); //分割两个序列继续进行快排操作 quickSort(a, low, pivot - 1); quickSort(a, pivot + 1, high); } } /** * @param a * @param low * @param high * @return */ public static int partition(int[] a, int low, int high) { //取每个序列的第一个值作为基准值 int pivotkey = a[low]; while (low < high) { //从序列的右边开始往左遍历,直到找到小于基准值的元素 while (high > low && a[high] >= pivotkey) { high--; } //将元素直接赋予给左边第一个,即pivotkey所在的位置 a[low] = a[high]; //a[high] = pivotkey; //从序列的左边边开始往右遍历,直到找到大于基准值的元素 while (high > low && a[low] <= pivotkey) { low++; } //此时的a[high]<pivotkey,已经被赋予到左边,所以可以将元素赋予给a[high] a[high] = a[low]; //a[low] = pivotkey; } //最后的low是基准值所在的位置 a[low] = pivotkey; return low; }}
如何合理取基准值pivotkey
- 该值的取值对该算法有相当影响,若pivotkey取到了最大或最小,都会增加算法复杂度,影响性能。
- 随机选取,在待排序列中随机选取,以降低取到最大或最小值的概率。
- 三数取中,在待排序列的左端,中间,右端去三个值选取中位数,节省随机数产生的时间开销,以降低取到最大或最小值的概率。三数取中时,比较的同时应将三个元素按中间,小,大的顺序重新排好位置。
- 九数取中,三次取样,每次取三个数,取它们的中位数,再取三个中位数的中位数。
参考
GeeksforGeeks:https://www.geeksforgeeks.org/quick-sort/
十大经典排序算法:https://www.cnblogs.com/onepixel/articles/7674659.html
转载自:https://juejin.cn/post/6844903640340267022