• 【算法】排序(五)快速排序


    正文之前

    快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),一种排序算法,最早由东尼 * 霍尔提出。在平均状况下,排序n个项目要O(nlogn)次比较,在最坏情况下则需要O(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他O(nlogn)算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。              ——Wikipedia


    本文将介绍以下内容

    排序原理
    算法实现(JAVA)
    测试阶段
    算法分析

    正文

    排序原理

    和归并排序划清界限:

    • 归并排序:将一个数组分成两个字数组,分别排序两部分,将有序的子数组归并得到整个有序数组

    • 快速排序:将一个数组分成两个字数组,分别排序两部分,当两个子数组有序时,整个数组自然就有序了

    快速排序使用一个切分元素来划分数组,切分元素左边的数组元素小于切分元素,右边的数组元素大于于切分元素,所以两个子数组有序就会得到整个有序的数组

    算法实现

    1. 切分数组
    private static int partition(int[] a, int low, int high){
            int i = low, j = high + 1;              //由两边向中间扫描
            int p = a[low];                         //切分数组的元素
            while(true){
                while(a[++i] < p){                  // i 的值会一直递增至 a[++i] >= p
                    if(i == high){                  //扫描结束
                        break;
                    }
                }
                while(a[--j] > p){                  // j 的值会一直递减至 a[++i] >= p
                    if(j == low){                   //扫描结束
                        break;
                    }
                }
    
                if(i >= j){                         // j 的位置就是切分元素在有序数组中的位置
                    break;
                }
    
                int temp = a[i];                    //交换元素顺序
                a[i] = a[j];
                a[j] = temp;
            }
    
            int temp = a[low];                      //将切分元素交换至中间位置
            a[low] = a[j];
            a[j] = temp;
    
            return j;
        }
    
    2. 数组排序

    这一部分的代码和归并排序的排序代码相似

    private static void quickSort(int[] a){
            sort(a, 0, a.length - 1);
        }
    
        private static void sort(int[] a, int lo, int hi){
            if(hi <= lo){
                return ;
            }
            int j = partition(a, lo, hi);
            sort(a, lo, j - 1);
            sort(a, j + 1, hi);
        }
    

    sort方法递归调用自己,将数组划分至最小,每次都将子序列中的第一个元素作为切分元素,来将数组排序

    整个排序过程的代码:
    public class QuickSort {
    
        private static int partition(int[] a, int low, int high){
            int i = low, j = high + 1;              //由两边向中间扫描
            int p = a[low];                         //切分数组的元素
            while(true){
                while(a[++i] < p){                  // i 的值会一直递增至 a[++i] >= p
                    if(i == high){                  //扫描结束
                        break;
                    }
                }
                while(a[--j] > p){                  // j 的值会一直递减至 a[++i] >= p
                    if(j == low){                   //扫描结束
                        break;
                    }
                }
    
                if(i >= j){                         // j 的位置就是切分元素在有序数组中的位置
                    break;
                }
    
                int temp = a[i];                    //交换元素顺序
                a[i] = a[j];
                a[j] = temp;
            }
    
            int temp = a[low];                      //将切分元素交换至中间位置
            a[low] = a[j];
            a[j] = temp;
    
            return j;
        }
    
        private static void quickSort(int[] a){
            sort(a, 0, a.length - 1);
        }
    
        private static void sort(int[] a, int lo, int hi){
            if(hi <= lo){
                return ;
            }
            int j = partition(a, lo, hi);
            sort(a, lo, j - 1);
            sort(a, j + 1, hi);
        }
    }
    

    测试阶段

    public static void main(String[] args){
            int[] a = new int[10];
            for (int i = 0; i < 10; i++) {
                a[i] = (int)(Math.random() * 100);
                System.out.print(a[i] + " ");
            }
            quickSort(a);
            System.out.println();
            for (int i = 0; i < 10; i++) {
                System.out.print(a[i] + " ");
            }
        }
    

    生成10个随机数字并调用快速排序方法,最后输出结果:

    算法分析

    1. 特点

    快速排序实现简单,应用广泛,比一般算法要快很多,并且是原地排序(借助辅助栈)

    2. 时间复杂度
    1. 设数组的长度为N,用 T(N) 表示排序一个长度为N的数组所需要的比较次数,可想而知,T(0) 和 T(1) 为0

    2. 切分的成本为N + 1,左子数组排序的平均成本为(C1 + C2 + ... + CN - 2 + CN - 1)/N,右子数组排序的平均成本为(CN - 1 + CN - 2 + ... + C2 + CN)/N

    3. CN = N + 1 + (C1 + C2 + ... + CN - 2 + CN - 1)/N + (CN - 1 + CN - 2 + ... + C2 + CN)/N

    4. 经过化简之后得到:CN ~ 2(N + 1)(1/3 + 1/4 + ... + 1/(N + 1))

    5. 积分得到CN ~ 2NlogN

    所以,算法复杂度为 O(nlogn)

    3. 稳定性

    举个例子,一个数组a = [6,4,3,2,3,7,8,7,9],a[0]和a[4]交换,第二个 3 就移动到了第一个 3 的前面,所以是不稳定的,不稳定性体现在切分元素交换的时候

    关于快速排序的介绍就到这里了,谢谢!

  • 相关阅读:
    Idea 设置
    git常用命令
    springBoot日志输出
    [ Linux ] 设置服务器开机自启端口
    [ Shell ] 通过 Shell 脚本导出 CDL 网表
    [ Shell ] 通过 Shell 脚本导出 GDSII/OASIS 文件
    浏览器下载文件乱码
    springbootmavenplugin 发布镜像至私服
    计算机CPU温度过高影响介绍
    计算机组成原理概述主机硬件拆分详解
  • 原文地址:https://www.cnblogs.com/lihanxiang/p/8460699.html
Copyright © 2020-2023  润新知