神級基礎排序——快速排序

概念

快速排序是一種原地排序,只需要一個很小的棧作爲輔助空間,空間複雜度爲O(logN),所以適合在數據集比較大且無序的時候使用。實現方法有經典快排和雙指針快排,本文介紹的是雙指針快排的實現。

【注意】:快排的經典排序實現的partition過程其實就是荷蘭國旗問題,不明白的或者不熟悉的可以先看一下荷蘭國旗算法。本文的實現主要講雙指針法。
荷蘭國旗算法:荷蘭國旗問題&快排&BFPRT算法 - god-jiang的文章 - 知乎

時間複雜度

時間複雜度比較複雜,最好的情況是O(N),最差的時候是O(N^2),所以平時說的O(N*logN)爲其平均時間複雜度。

基本思想

隨機找出一個數,可以隨機取,也可以取固定位置,一般是取第一個或最後一個稱爲基準,然後就是比基準小的放在左邊,比基準大的放到右邊。如何放呢?就是和基準進行交換,這樣交換完左邊都是比基準小的,右邊都是比較基準大的,這樣就將一個數組分成了兩個子數組,然後再按照同樣的方法把子數組再分成更小的子數組,直到不能分解爲止。

操作實現

partition方法中

1.選擇數組中的第一個元素arr[startIndex]作爲軸(pivot)

2.右指針爲right,從最右面的一個元素開始向左尋找第一個小於等於pivot的數值

3.左指針爲left,從最左邊開始尋找第一個比pivot大的數

4.經過2,3兩個步驟後,將會出現以下兩種情況
​ (1):left和right沒有相遇,此時進行交換,swap(arr,left,right);
​ (2):left和right相遇,做swap(arr,startIndex,left),然後返回left

5.partition中返回pivot用於分割數組,下一次用於排序的數組被分割爲(startIndex,pivot-1),(pivot+1,endIndex)兩段,進行遞歸操作
過程演示

給定一個原始數組爲:
在這裏插入圖片描述

開始時,我們首先選擇第一個數作爲基準pivot,並且設置兩個指針left和right,指向數組的最左位置和最右位置:
在這裏插入圖片描述

從right位置開始和基準pivot比較,直到right對應的那個數比基準pivot小的時候停下;然後從left位置開始和基準pivot比較,直到left對應的那個數比基準pivot大的時候停下。因爲right對應的1比4小,所以停下不動;left對應的是4沒有大於4,所以向右移動,然後left對應的是7比4大,所以停下不動:
在這裏插入圖片描述

這時交換left和right的位置:
在這裏插入圖片描述

接下來繼續重複剛纔的過程,right向左移動到8比4大,繼續移動到2,比4小,所以停下;然後left向右移動到6比4大,所以停下:
在這裏插入圖片描述

這時交換left和right的位置:
在這裏插入圖片描述

繼續重複上面的過程,right向左移動到3比4小,所以停下;left向右移動到5比4大,所以停下:
在這裏插入圖片描述

這時交換left和right的位置:
在這裏插入圖片描述

right向左移動和left指針重合,這個時候停下:
在這裏插入圖片描述

當left和right重合的時候,我們把pivot位置和left位置進行交換,這個時候就是一趟快排的結果,基準pivot的值是4,它的左邊都比4小,它的右邊都比4大:
在這裏插入圖片描述

然後就通過分治遞歸的方法,分別對左邊和右邊繼續剛纔的過程,直到數組不能再分爲止。

代碼實現

import java.util.Arrays;
/**
 * @author god-jiang
 * @date 2020/1/12
 */
//時間複雜度O(n*logn),空間複雜度O(n*logn)
public class QuickSort {
    public static void quickSort(int[] arr, int startIndex, int endIndex) {
        if (startIndex < endIndex) {
            //找出基準
            int partition = partition(arr, startIndex, endIndex);
            //分成兩邊遞歸進行
            quickSort(arr, startIndex, partition - 1);
            quickSort(arr, partition + 1, endIndex);
        }
    }

    //找基準
    private static int partition(int[] arr, int startIndex, int endIndex) {
        int pivot = arr[startIndex];
        int left = startIndex;
        int right = endIndex;
        while (left != right) {
            while (left < right && arr[right] > pivot) {
                right--;
            }
            while (left < right && arr[left] <= pivot) {
                left++;
            }
            //找到left比基準大,right比基準小,進行交換
            if (left < right) {
                swap(arr, left, right);
            }
        }
        //第一輪完成,讓left和right重合的位置和基準交換,返回基準的位置
        swap(arr, startIndex, left);
        return left;
    }

    //兩數交換
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    public static void main(String[] args) {
        int[] a = {2, 4, 6, 1, 3, 7, 9, 8, 5};
        quickSort(a, 0, a.length - 1);
        System.out.println(Arrays.toString(a));
    }
}

運行截圖

在這裏插入圖片描述

1、以上就是今天分享的快速排序,時間複雜度爲O(NlogN),空間複雜度爲O(NlogN)
2、快排是不穩定排序,要想做到穩定性是可以的,但是非常難,不需要掌握,有一篇論文叫”01 stable sort”可以做到
3、有一道題目,是奇數放數組左邊,偶數放在數組右邊,還要求原始的相對次序不變,額外空間複雜度爲O(1),碰到這個問題,直接可以懟面試官,根本不可能做出來

PS:因爲博主畫圖實在太醜了,所以去了“程序員小灰”的公衆號copy了他的圖片來講解快排的整個過程。覺得博主寫的還不錯的點點贊,關注走一波,謝謝大家的支持了,我們一起進步吧~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章