首页 > 代码库 > 交换排序-快速排序

交换排序-快速排序


复杂的案例
[9, 3, 5, 7, 5, 1, 6, 13, 0, 4]
[4, 3, 5, 7, 5, 1, 6, 13, 0, 9]
[4, 3, 5, 7, 5, 1, 6,  90, 13]
第一趟交换完毕,然后会将原数组分割为两个数组,其中【基准元素】9已经是在正确的位置了

先对左半部分[4, 3, 5, 7, 5, 1, 6, 0]排序
[4, 3, 5, 7, 5, 1, 6, 09, 13]
[0, 3, 5, 7, 5, 1, 6, 49, 13]
[0, 3, 4, 7, 5, 1, 6, 5, 9, 13]
[0, 3, 1, 7, 5, 4, 6, 5, 9, 13]
再对右半部分[13]排序(右半部分不需要排序)
然后对【左半部分的左半部分】即[0,3,1]排序
不过【左半部分的左半部分】排序后没结果(因为基准元素 0 已经是在正确的位置了
所以又把[0,3,1]分成了两组,其中左半部分[0]不需要排序
所以对右半部分[3,1]排序
[03145, 7, 6, 5, 9, 13]

下面对左半部分的右半部分】即[5,7,6,5]排序
但是排序后没结果(因为基准元素 5 已经是在正确的位置了
所以又把[5,7,6,5]分成了两组,其中左半部分[5]不需要排序
下面是对右半部分[7,6,5]排序
[0, 1, 3, 457, 6, 59, 13]
[0, 1, 3, 455, 6, 7, 9, 13]

代码
排序的全过程
[49, 38, 65, 97, 76, 13, 27, 49]--从【右】端开始比较,如果【右】端的元素【小】于基准元素,则交换,所以会把【右】端的27和基准元素49交换
[27, 38, 65, 97, 76, 13, 49, 49]--从【左】端开始比较,如果【左】端的元素【大】于基准元素,则交换,所以会把【左】端的65和基准元素49交换
[27, 38, 49, 97, 76, 13, 65, 49]--从【右】端开始比较,如果【右】端的元素【小】于基准元素,则交换,所以会把【右】端的13和基准元素49交换
[27, 38, 13, 97, 76, 49, 65, 49]--从【左】端开始比较,如果【左】端的元素【大】于基准元素,则交换,所以会把【左】端的97和基准元素49交换
----------------第一趟交换完毕,然后会将原数组分割为两个数组,其中【基准元素】49已经是在正确的位置了

先排序左半部分
[27, 38, 1349, 76, 97, 65, 49]-从【右】端开始比较,如果【右】端的元素【小】于基准元素,则交换,所以会把【右】端的13和基准元素27交换
[13, 382749, 76, 97, 65, 49]-从【左】端开始比较,如果【左】端的元素【大】于基准元素,则交换,所以会把【左】端的38和基准元素27交换
左半部分已经完成排序,再排序右半部分
[13, 27, 38, 4976, 97, 65, 49]--从【右】端开始比较,如果【右】端的元素【小】于基准元素,则交换,所以会把【右】端的49和基准元素76交换
[13, 27, 38, 49, 49, 97, 65, 76]--从【左】端开始比较,如果【左】端的元素【大】于基准元素,则交换,所以会把【左】端的97和基准元素76交换
[13, 27, 38, 49, 49, 7665, 97]--从【右】端开始比较,如果【右】端的元素【小】于基准元素,则交换,所以会把【右】端的65和基准元素76交换
[13, 27, 38, 49, 49, 65, 76, 97]
左右半部分都已【递归】排序完毕

public class Test {
    public static void main(String[] args) throws Exception {
        int[] arr = { 49, 38, 65, 97, 76, 13, 27, 49 };
        System.out.println(Arrays.toString(arr));
        qsort(arr, 0, arr.length - 1);
    }
    /**
     * 快速排序<br>冒泡排序的升级版,现在用的最多的排序方法<br>不稳定排序算法<br>
     * 时间复杂度:最好:O(nlogn),最坏:O(n^2)  <br>空间复杂度:O(logn)  
     * @param arr    要排序的数组
     * @param low    对此数组指定的范围排序
     * @param high    对此数组指定的范围排序
     */
    public static void qsort(int[] arr, int low, int high) {
        if (low < high) {
            int pivot = partition(arr, low, high);//将表一分为二
            qsort(arr, low, pivot);//【递归】对低子表【递归】排序
            qsort(arr, pivot + 1, high);//递归对高子表递归排序
        }
    }
    /**
     * 对数组进行分割
     */
    private static int partition(int[] arr, int low, int high) {
        int pivotkey = arr[low];//选择一个【基准元素】,通常选择第一个元素或者最后一个元素
        while (low < high) {//从表的两端【交替】地向中间扫描
            while (low < high && arr[high] >= pivotkey) {
                high--;
            }
            swap(arr, low, high);//将比基准元素小的交换到低端
            while (low < high && arr[low] <= pivotkey) {
                low++;
            }
            swap(arr, low, high);//将比基准元素大的交换到高端
        }
        return low;//此时基准元素在其排好序后的正确位置
    }
    public static void swap(int[] arr, int i, int j) {
        if (i == j) return;
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
        System.out.println(Arrays.toString(arr));
    }
}

原理
基本思想:1)选择一个基准元素,通常选择第一个元素或者最后一个元素,2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大。3)此时基准元素在其排好序后的正确位置4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
一趟排序的过程:
技术分享

排序全过程:
技术分享

分析:快速排序是通常被认为在同数量级(O(nlog2n))的排序方法中平均性能最好的。但若初始序列按关键码有序或基本有序时,快排序反而蜕化为冒泡排序。为改进之,通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录
快速排序是一个不稳定的排序方法。



来自为知笔记(Wiz)


交换排序-快速排序