欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

排序算法:快速排序

程序员文章站 2022-03-24 15:33:30
...

Sorting Algorithms:Quick Sort

前言

该博客用于本弱鸡复习巩固,打牢基础,还望各大佬不吝赐教。

基本思路

1.对一个未排序序列,假设从该序列中的元素中取一个基准值pivotkey,将小于pivotkey放左边,大于pivotkey放右边;
2.接着以该k为中间,左右两边的分割作为新的序列,重新进行1操作。
快排因为用到了递归操作,所以在简单排序中性能不如直接插入排序,
而在大量数据排序时,递归产生的性能影响对于算法的整体性能优势可以忽略。

动图示例

Quick Sort

算法复杂度分析

平均 最坏 最好 稳定性 空间复杂度
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://book.douban.com/subject/6424904/