快速排序及其優化--隨機快速排序,二路快速排序,三路快速排序(Java實現))

排序算法名稱 針對的應用情景
快速排序 無序素組(對于基本有序數組和大量重復鍵值的數組復雜度上升至O(n2)
隨機速排 快速排序的優化,解決普通快排在部分有序數組中進行排序,每次取得的都是趨近最值
二路快排 隨機速排的優化,解決數組中存在大量鍵值重復的情況以及基本有序數組
三路快排 二路排序的優化,把等于value的元素放在另一個區間內,不參與下次的排序。

從上到下都是基于上面的排序算法進行優化

swap方法原型

/**
     * 沒有辦法想C語言的指針那樣直接通過指針交換,可以通過數組或者是對象屬性來交換
     * @param arr  數組名
     * @param x 下標
     * @param y 下標
     */
    public static void swap(int[] arr, int x, int y){
        int temp;
        temp  = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }

Java快速排序

ackage sort;

import org.junit.Test;

import static sort.PublicMethod.swap;

/**
 * @author panghu
 * @title: QuickSort
 * @projectName Algorithm_And_Data_Structure
 * @date 19-6-5 下午8:05
 */
public class QuickSort {


    private void quickSort(int[] arr, int l, int r) {

        if (l >= r){
            return;
        }
        int position = partition(arr,l,r);
        quickSort(arr,l,position-1);
        quickSort(arr,position+1,r);
    }

    /*
    * 對arr[l...r]部分進行partition操作
    * 返回position,是的arr[l...p-1]<arr[p],arr[p+1...r]>arr[p]
    * */
    public static int partition(int[] arr,int left,int right){

        int value = arr[left];

        int position = left;
        //這里的right值是最右邊的值 arr[right]是有效的
        for (int i=left+1;i<=right;i++){
            if (arr[i] < value){
                /*
                * 相關的操作
                * 1.比初始位置大的數都放在初始位置的右邊一個,放一個position的位置增加一
                * */
                swap(arr,i,++position);
            }
        }

        //走到這一步的時候  arr[l]存放的是分解值,arr[position]存放的是小于分界值
        swap(arr,left,position);
        return position;

    }

    @Test
    public void test(){
        int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
        quickSort(a,  0,a.length-1);
        System.err.println("排好序的數組:");
        for (int e : a) {
            System.out.print(e+" ");
        }
    }

}

快速排序算法

思路:

  • 從序列中挑選出一個元素(一般是第一個或者是最后一個)作為"基準"元素
  • 把序列分成2個部分,其數值大于"基準"元素的元素放在"基準"元素的左邊,否在放在"基準"元
    素的右邊,此時"基準"元素所在的位置就是正確的排序位置,這個過程被稱為 partition(分區)
  • 遞歸將"基準"元素左邊的序列和"基準"元素右邊的序列進行partition操作

缺點:

  • 當partition操作時,如果每次取得的的第一個元素趨近于最值,使得分治的任務極度不平衡,情況最壞時,快速排序算法的復雜度將上升到O(n2)**
  • 存在大量重復鍵值時,同樣會出現分治任務很不平衡的情況

隨機快速排序算法

package sort;

import org.junit.Test;

import static sort.PublicMethod.swap;

/**
 * @author panghu
 * @title: RandomQuickSort
 * @projectName Algorithm_And_Data_Structure
 * @date 19-7-21 下午9:49
 */
public class RandomQuickSort {

    private void quickSort(int[] arr, int l, int r) {

        if (l >= r){
            return;
        }
        int position = partition(arr,l,r);
        quickSort(arr,l,position-1);
        quickSort(arr,position+1,r);
    }

    /*
     * 對arr[l...r]部分進行partition操作
     * 返回position,是的arr[l...p-1]<arr[p],arr[p+1...r]>arr[p]
     * */
    public static int partition(int[] arr,int left,int right){

        int value = arr[left];
        //這個地方是唯一的區別.在left~right之間生成一個隨機數
        int ranNum = left + (int)(Math.random()*(right-left+1));
        //把隨機數作為索引,將索引對應值與最后一位值 right 交換
        swap(arr,right,ranNum);
        int position = left;
        //這里的right值是最右邊的值 arr[right]是有效的
        for (int i=left+1;i<=right;i++){
            if (arr[i] < value){
                /*
                 * 相關的操作
                 * 1.比初始位置大的數都放在初始位置的右邊一個,放一個position的位置增加一
                 * */
                swap(arr,i,++position);
            }
        }

        //走到這一步的時候  arr[l]存放的是分解值,arr[position]存放的是小于分界值
        //自我感覺這一步  有一種一舉兩得,即將分界值的位置移到了正確位置,也將左值放在了左邊
        swap(arr,left,position);
        return position;

    }

    @Test
    public void test(){
        int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
        quickSort(a,  0,a.length-1);
        System.err.println("排好序的數組:");
        for (int e : a) {
            System.out.print(e+" ");
        }
    }


}

思路:

  • 在每次partition的過程中,將leftright-1之間的隨機選取一個元素,與right進行位置交換,這樣就解決了快排中如果數組部分有序,數組劃分不平衡的情況

缺點

  • 當數組中存在大量重復鍵值的時候任然會出現算法復雜度上升的情況

兩路快速排序算法

package sort;

import org.junit.Test;

import static sort.PublicMethod.swap;

/**
 * @author panghu
 * @title: QuickSort2
 * @projectName Algorithm_And_Data_Structure
 * @date 19-7-22 下午10:27
 */
public class QuickSort2 {

    private void quickSort(int[] arr, int l, int r) {

        if (l >= r){
            return;
        }
        int position = partition(arr,l,r);
        quickSort(arr,l,position-1);
        quickSort(arr,position+1,r);
    }

    int partition(int[] arr, int left, int right){
        int ranNum = left + (int)(Math.random()*(right-left+1));
        //把隨機數作為索引,將索引對應值與最后一位值 right 交換
        swap(arr,right,ranNum);
        // arr[left+1...i) <= v; arr(j...right] >= v
        int value = arr[left];

        int i = left+1, j = right;
        while( true ){
            while( i <= right && arr[i] < value) {
                i ++;
            }

            while( j >= left+1 && arr[j] > value ) {
                j --;
            }

            if( i > j ) {
                break;
            }

            swap(arr,i,j);
            i ++;
            j --;
        }

        swap(arr, left, j);

        return j;
    }


    @Test
    public void test(){
        int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
        quickSort(a,  0,a.length-1);
        System.err.println("排好序的數組:");
        for (int e : a) {
            System.out.print(e+" ");
        }
    }


}
二路快速排序
二路快速排序

思路:

  • 從左邊和右邊分別遍歷,當左邊遍歷到第i位置時,所對應的元素大于v,從右邊遍歷的元素遍歷到第j個位置時,所對應的元素arr[j]<v,交換i和j的位置直到i>j.適用于有大量重復鍵值的情形和數組基本有序的問題

package sort;

import org.junit.Test;

import static sort.PublicMethod.swap;

/**
 * @author panghu
 * @title: QuickSort3
 * @projectName Algorithm_And_Data_Structure
 * @date 19-7-23 下午2:02
 */
public class QuickSort3 {

    private void quickSort(int[] arr, int l, int r) {

        if (l >= r){
            return;
        }
        int position = partition(arr,l,r);
        quickSort(arr,l,position-1);
        quickSort(arr,position+1,r);
    }

    int partition(int[] arr,int left,int right){

        int ranNum = left + (int)(Math.random()*(right-left+1));
        //把隨機數作為索引,將索引對應值與最后一位值 right 交換
        swap(arr,right,ranNum);
        // arr[left+1...i) <= value; arr(j...right] >= value
        int value = arr[left];
        // 將<v的分界線的索引值lt初始化為第一個元素的位置(也就是<v部分的最后一個元素所在位置)
        int lt = left;
        //將>value的分界線的索引值gt初始化為最后一個元素right的后一個元素所在位置
        // (也就是>value部分的第一個元素所在位置)
        int gt = right+1;
        // 將遍歷序列的索引值i初始化為 left+1
        int i = left+1;
        while (i < gt){
            if (arr[i] < value){
                swap(arr, lt+1, i);
                //移動指針
                i++;
                //增加<value的部分
                lt++;
            }else if ( arr[i] > value){
                swap(arr,gt-1,i);
                //增加>value的部分
                gt--;
                //注意,這里不需要移動i,之前移動i是因為需要增加<value的部分
                //而保持=i部分不懂,所以i和lt都需要移動

            }else{
                //增加=v的部分
                i++;
            }
        }
        return i;

    }

    @Test
    public void test(){
        int[] a = { 49, 38, 65, 97, 76, 13, 27, 50 };
        quickSort(a,  0,a.length-1);
        System.err.println("排好序的數組:");
        for (int e : a) {
            System.out.print(e+" ");
        }
    }

}

三路快速排序算法

三路快速排序算法
三路快速排序算法
三路快速排序算法
三路快速排序算法

思路:

  • 之前的快速排序是將數組劃分為 分成<=v和>v或者是<v和>=v的兩個部分,而三路快排是將數組劃分為分成三個部分:`<v、=v、>v

應用演示

package leetcode;

/**
 * 題目要求:給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,
 * 使得相同顏色的元素相鄰,并按照紅色、白色、藍色順序排列。
 * 此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。
 */

import java.util.Arrays;

/**
 * @author panghu
 * @title: Solution75
 * @projectName Algorithm_And_Data_Structure
 * @date 19-7-16 下午10:06
 */



public class Solution75 {
      **
     * 三路快速排序法的應用
     * @param nums 傳入的數組
     */
    static void sortColors(int[] nums){
        // nums[0...zero] == 0
        int zero = -1;
        // nums[two..n-1] == 2
        int two = nums.length;
        for (int i = 0;i < two;){
            if (nums[i] == 1){
                i++;
            }else if (nums[i] == 2){
                two--;
                swap(nums,i,two);
            }else {
                if (nums[i] != 0){
                    throw new IllegalArgumentException("數組中的值只能為1,2,3");
                }
                zero++;
                swap(nums,i,zero);
            }
        }
    }

    /**
     * 通過數組交換數值
     * @param arr  數組
     * @param x  數組索引1
     * @param y  數組索引2
     */
    static void swap(int[] arr,int x,int y){
        //temp用來臨時存儲值
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }

    public static void main(String[] args) {
        int[] arr = {1,2,1,2,1,0,0,0,0,0};
        sortColors(arr);
        System.out.println(Arrays.toString(arr));
    }
}

參考博客:https://www.cnblogs.com/deng-tao/p
參考課程:https://coding.imooc.com/class/chapter/71.html#Anchor

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • 搞懂基本排序算法 上篇文章寫了關于 Java 內部類的基本知識,感興趣的朋友可以去看一下:搞懂 JAVA 內部類;...
    醒著的碼者閱讀 1,203評論 3 4
  • quicksort可以說是應用最廣泛的排序算法之一,它的基本思想是分治法,選擇一個pivot(中軸點),將小于pi...
    黎景陽閱讀 456評論 0 1
  • 參考:十大經典排序算法 0、排序算法說明 0.1排序的定義 對一序列對象根據某個關鍵字進行排序。 0.2 術語說明...
    誰在烽煙彼岸閱讀 1,021評論 0 12
  • 1.插入排序—直接插入排序(Straight Insertion Sort) 基本思想: 將一個記錄插入到已排序好...
    依依玖玥閱讀 1,266評論 0 2
  • 常常有些越過控制的事能讓我變成祥林嫂,無法只是想表達,其實對于大家的反應我一點也不在乎.何必呢!只是需要出口時,我...
    040a86361a12閱讀 172評論 0 0