快速排序

什么是快速排序

快速排序(Quicksort)雇盖,計算機科學詞匯睛廊,適用領(lǐng)域Pascal藐不,c++等語言莉兰,是對冒泡排序算法的一種改進浅乔。

快速排序的思想

快速排序算法通過多次比較和交換來實現(xiàn)排序倔喂,其排序流程如下:

  1. 首先設(shè)定一個分界值铝条,通過該分界值將數(shù)組分成左右兩部分。

  2. 將大于或等于分界值的數(shù)據(jù)集中到數(shù)組右邊席噩,小于分界值的數(shù)據(jù)集中到數(shù)組的左邊班缰。此時,左邊部分中各元素都小于分界值悼枢,而右邊部分中各元素都大于或等于分界值埠忘。

  3. 然后,左邊和右邊的數(shù)據(jù)可以獨立排序馒索。對于左側(cè)的數(shù)組數(shù)據(jù)莹妒,又可以取一個分界值,將該部分數(shù)據(jù)分成左右兩部分绰上,同樣在左邊放置較小值旨怠,右邊放置較大值。右側(cè)的數(shù)組數(shù)據(jù)也可以做類似處理蜈块。

  4. 重復上述過程鉴腻,可以看出,這是一個遞歸定義百揭。通過遞歸將左側(cè)部分排好序后爽哎,再遞歸排好右側(cè)部分的順序。當左信峻、右兩個部分各數(shù)據(jù)排序完成后倦青,整個數(shù)組的排序也就完成了瓮床。

圖解快速排序

image.png

快速排序的步驟

  1. 交換函數(shù)

    這個函數(shù)就不多說了盹舞,就是交換兩個元素位置的函數(shù)。

        /**
         * 交換某兩個位置元素的函數(shù)
         * @param arr
         * @param i
         * @param j
         */
        public static void swap(int[] arr, int i, int j){
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    
  2. 選取基準值的函數(shù)

    選取一個基準值隘庄,然后把比基準值大的移動到基準值的右邊踢步,比基準值小的移動到基準值的左邊,最后返回基準值的位置丑掺。示例方法中是使用默認去第一個获印,這種方法是有缺陷的,基準值不夠隨機街州,對于部分場景可能會效率低兼丰,文章最后會做分析和提出解決方案。

     /**
         * 數(shù)組的最低位和最高位
         * @param arr
         * @param lo
         * @param hi
         *///partition
        public static int partition(int[] arr, int lo, int hi){
    
            //低位給i,高位加一給 j ,為什么這里高位要 +1 因為下面的用法是 --j , 為什么地位不需要 -1 ,下面用的是 --i,
            //因為已經(jīng)提前把低位的值給拿出來了,第一個比較的就是 arr[++i] 元素
            int i = lo, j = hi + 1;
            //這里是直接取了第一個作為基準值,如果是已經(jīng)有序的數(shù)組,會導致時間復雜度很高
            int v = arr[lo];
            //從小到大排序
            while(true){
                //左邊的指針找到一個比自己大的,然后停止循環(huán)
                while(v >= arr[++i]){
                    if(i >= hi) break;
                }
                //右邊的指針找到一個比自己小的,然后停止循環(huán)
                while(v <= arr[--j]){
                    if(j <= lo) break;
                }
                //如果左指針 大于等于 右指針,則跳出循環(huán)
                if(i >= j){
                    break;
                }
                //交換左指針指向的比自己大的和右指針指向的比自己小的元素的位置
                swap(arr,i,j);
            }
            //交換最低位元素和i,j位置元素
            swap(arr,lo,j);
            //范圍基準值的位置
            return j;
        }
    
  1. 排序函數(shù)

    整個排序方法的入口唆缴,根據(jù)調(diào)整后的基準值來作為界限鳍征,分別排序基準值兩側(cè)的子序列

         //排序函數(shù)
        /**
         * 方法是遞歸調(diào)用,每次都是按照基準值調(diào)整完元素后,把基準值兩次的元素繼續(xù)按照此方法來排序
         * @param arr
         * @param lo
         * @param hi
         */
        public static void sort(int[] arr, int lo, int hi){
            //低位不大于等于高位
            if(lo >= hi) return;
            //獲取基準值
            int partition= partition(arr,lo,hi);
            //把基準值兩側(cè)的元素繼續(xù)按照快速排序的方法排序
            sort(arr, lo, partition - 1);
            sort(arr, partition+1,hi);
        }
    

完整的代碼

package demo.off;

import javax.sound.midi.Soundbank;
import java.util.Arrays;

public class kuaisupaixu {
    public static void main(String[] args) {
        int[] arr ={7,5,32,1,4,5,6,7,8,9,8,77,66,55,22,10};
        sort(arr, 0, arr.length-1);
        System.out.println(Arrays.toString(arr));
    }
    //排序函數(shù)
    /**
     * 測方法是遞歸調(diào)用,每次都是按照基準值調(diào)整完元素后,把基準值兩次的元素繼續(xù)按照此方法來排序
     * @param arr
     * @param lo
     * @param hi
     */
    public static void sort(int[] arr, int lo, int hi){
        //低位不大于等于高位
        if(lo >= hi) return;
        //獲取基準值
        int partition= partition(arr,lo,hi);
        //把基準值兩側(cè)的元素繼續(xù)按照快速排序的方法排序
        sort(arr, lo, partition - 1);
        sort(arr, partition+1,hi);
    }

    //swap
    /**
     * 交換某兩個位置元素的函數(shù)
     * @param arr
     * @param i
     * @param j
     */
    public static void swap(int[] arr, int i, int j){
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

    /**
     * 數(shù)組的最低位和最高位
     * @param arr
     * @param lo
     * @param hi
     *///partition
    public static int partition(int[] arr, int lo, int hi){

        //低位給i,高位加一給 j ,為什么這里高位要 +1 因為下面的用法是 --j , 為什么地位不需要 -1 ,下面用的是 --i,
        //因為已經(jīng)提前把低位的值給拿出來了,第一個計較的就是 arr[++i] 元素
        int i = lo, j = hi + 1;
        //這里是直接取了第一個作為基準值,如果是已經(jīng)有序的數(shù)組,會導致時間復雜度很高
        int v = arr[lo];
        //從小到大排序
        while(true){
            //左邊的指針找到一個比自己大的,然后停止循環(huán)
            while(v >= arr[++i]){
                if(i >= hi) break;
            }
            //右邊的指針找到一個比自己小的,然后停止循環(huán)
            while(v <= arr[--j]){
                if(j <= lo) break;
            }
            //如果左指針 大于等于 右指針,則跳出循環(huán)
            if(i >= j){
                break;
            }
            //交換左指針指向的比自己大的和右指針指向的比自己小的元素的位置
            swap(arr,i,j);
        }
        //交換最低位元素和i,j位置元素
        swap(arr,lo,j);
        //范圍基準值的位置
        return j;
    }
}

排序結(jié)果

排序前

int[] arr ={7,5,32,1,4,5,6,7,8,9,8,77,66,55,22,10};

排序后

[1, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 22, 32, 55, 66, 77]

快速排序的缺點

盡管快速排序有很多優(yōu)點,它的基本實現(xiàn)仍有一個潛在的缺點:在切分不平衡時這個程序可能會極為低效面徽。例如艳丛,如果第一次從最小的元素切分匣掸,第二次從第二小的元素切分,如此這般氮双,每次調(diào)用只會移除一個元素碰酝。這會導致一個大子數(shù)組需要切分很多次。我們要在快速排序前將數(shù)組隨機排序的主要原因就是要避免這種情況戴差。它能夠使產(chǎn)生糟糕的切分的可能性降到極低送爸,我們就無需為此擔心了。[1]

快速排序的改進方案

為了讓基準值更加的隨機暖释,可以取高位碱璃,低位,中位中間的中位數(shù)饭入,來增加基準值的隨機性嵌器。

    
      //計算中位數(shù)并且交換位置
        if(arr[hi] > arr[mid] && arr[i] < arr[mid]){
            //mid 位置是三個數(shù)中的中位數(shù)
            swap(arr,i,mid);
        }else if(arr[i] > arr[hi] && arr[i] < arr[mid]){
            //i就是中位數(shù),不需要交換
            //mid = i;
        }else{
            //j位置是中位數(shù),需要交換位置
            swap(arr,i,hi);
        }

完整的partition代碼:

/**
     * 數(shù)組的最低位和最高位
     * @param arr
     * @param lo
     * @param hi
     *///partition
    public static int partition(int[] arr, int lo, int hi){

        //低位給i,高位加一給 j ,為什么這里高位要 +1 因為下面的用法是 --j , 為什么地位不需要 -1 ,下面用的是 --i,
        //因為已經(jīng)提前把低位的值給拿出來了,第一個計較的就是 arr[++i] 元素
        int i = lo, j = hi + 1 ,mid = (lo + hi)/2;
        
        //計算中位數(shù)并且交換位置
        if(arr[hi] > arr[mid] && arr[i] < arr[mid]){
            //mid 位置是三個數(shù)中的中位數(shù)
            swap(arr,i,mid);
        }else if(arr[i] > arr[hi] && arr[i] < arr[mid]){
            //i就是中位數(shù),不需要交換
            //mid = i;
        }else{
            //j位置是中位數(shù),需要交換位置
            swap(arr,i,hi);
        }

        //這里是直接取了第一個作為基準值,如果是已經(jīng)有序的數(shù)組,會導致時間復雜度很高
        int v = arr[lo];
        //從小到大排序
        while(true){
            //左邊的指針找到一個比自己大的,然后停止循環(huán)
            while(v >= arr[++i]){
                if(i == hi) break;
            }
            //右邊的指針找到一個比自己小的,然后停止循環(huán)
            while(v <= arr[--j]){
                if(j == lo) break;
            }
            //如果左指針 大于等于 右指針,則跳出循環(huán)
            if(i >= j){
                break;
            }
            //交換左指針指向的比自己大的和右指針指向的比自己小的元素的位置
            swap(arr,i,j);
        }
        System.out.println(i);
        System.out.println(j);
        //交換最低位元素和i,j位置元素
        swap(arr,lo,j);
        //范圍基準值的位置
        return j;
    }

快速排序和歸并排序

快速排序是一種分治的排序算法。它將一個數(shù)組分成兩個子數(shù)組谐丢,將兩部分獨立地排序爽航。快速排序和歸并排序是互補的:歸并排序?qū)?shù)組分成兩個子數(shù)組分別排序乾忱,并將有序的子數(shù)組歸并以將整個數(shù)組排序讥珍;而快速排序?qū)?shù)組排序的方式則是當兩個子數(shù)組都有序時整個數(shù)組也就自然有序了。在第一種情況中窄瘟,遞歸調(diào)用發(fā)生在處理整個數(shù)組之前衷佃;在第二種情況中,遞歸調(diào)用發(fā)生在處理整個數(shù)組之后蹄葱。在歸并排序中氏义,一個數(shù)組被等分為兩半;在快速排序中图云,切分(partition)的位置取決于數(shù)組的內(nèi)容惯悠。[1]


  1. 算法(第四版) 2.3 快速排序 ? ?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市竣况,隨后出現(xiàn)的幾起案子克婶,更是在濱河造成了極大的恐慌,老刑警劉巖丹泉,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件情萤,死亡現(xiàn)場離奇詭異,居然都是意外死亡摹恨,警方通過查閱死者的電腦和手機筋岛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來睬塌,“玉大人泉蝌,你說我怎么就攤上這事歇万。” “怎么了勋陪?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵贪磺,是天一觀的道長。 經(jīng)常有香客問我诅愚,道長寒锚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任违孝,我火速辦了婚禮刹前,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雌桑。我一直安慰自己喇喉,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布校坑。 她就那樣靜靜地躺著拣技,像睡著了一般。 火紅的嫁衣襯著肌膚如雪耍目。 梳的紋絲不亂的頭發(fā)上膏斤,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機與錄音邪驮,去河邊找鬼莫辨。 笑死,一個胖子當著我的面吹牛毅访,可吹牛的內(nèi)容都是我干的沮榜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼俺抽,長吁一口氣:“原來是場噩夢啊……” “哼敞映!你這毒婦竟也來了较曼?” 一聲冷哼從身側(cè)響起磷斧,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎捷犹,沒想到半個月后弛饭,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡萍歉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年侣颂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枪孩。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡憔晒,死狀恐怖藻肄,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拒担,我是刑警寧澤嘹屯,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站从撼,受9級特大地震影響州弟,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜低零,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一婆翔、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧掏婶,春花似錦啃奴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至茎芭,卻和暖如春揖膜,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背梅桩。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工壹粟, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人宿百。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓趁仙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親垦页。 傳聞我的和親對象是個殘疾皇子雀费,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容