快速排序的效能和名字一樣優秀

Code綜藝圈發表於2021-05-07

前言

上次分享的氣泡排序雖然比較簡單、容易理解,但每一次冒泡的過程都需要依次比較相鄰的元素,然後交換,可見效能還是有很大的優化空間,只要能減少比較次數,效能自然就上去啦;快速排序便是一個很不錯的選擇~~~

正文

1.1 快速排序演算法思想

快速排序(Quicksort)是對上一次分享的氣泡排序演算法的一種改進,主要是減少比較次數,以此來提高排序效能;也屬於交換排序的一種。

演算法思想

  • 在待排序列表中任取一個元素作為基準值
  • 將剩下的元素和基準值依次比較,小於的放左邊,大於的放右邊,最後通過一趟排序將待排序列表分為左右兩部分,這個過程稱為一次“劃分”
  • 然後用同樣的方法分別在左右兩部分中取基準值進行排序,依次遞迴,直到劃分的每部分內只有一個元素或空為止,則排序結束。

1.2 快速排序演算法實現與解析

  • 演算法實現

    劃分程式碼實現,每一次取對應部分的首位元素作為基準值,然後通過依次比較,將對應資料又在細分為左右兩部分,如下實現:

    image-20210506133026759

    使用遞迴進行劃分排序,直到只有一個元素或為空為止:

    image-20210506133313691

  • 執行效果

    image-20210506133449121

  • 步驟解析(升序)

    image-20210506135153320

    上圖步驟說明:

    上圖演示是針對陣列array進行升序排序,第一次以全部資料為一組,low為0,high為5,選擇low位元素2為基準值(即第一個元素),開始進行比較:

    第1.1步:由於剛開始選擇low位的元素為基準值(可以認為這個位置空了),所以接下來開始從high位開始遍歷比較,high位元素3大於2,不用交換位置,high減1,繼續比較;

    第1.2步:此時low為0,high為4,high位元素9大於2,不用交換位置,high減1,繼續比較;

    第1.3步:此時low為0,high為3,high位元素1小於2,需要將high位元素1放到基準值的左邊,則將元素1放到low指向的位置;

    第1.4步:此時low為0,high為3,由於high位已經交換過了(可以認為這個位置空了),所以這次開始到low位進行遍歷比較,low位元素是剛交換過來的,所以不用交換位置;low加1繼續比較,此時low為1,對應位置的元素5大於基準值2,所以需要將元素5放到基準值的右邊,則將元素5放到high指向位置;

    第1.5步:此時low為1,high為3,由於low位已經交換過了(可以認為這個位置空了),所以這次又回到high位進行遍歷比較,high位元素是剛交換過來的,所以不用交換位置;high減1繼續比較,此時high為2,對應位置元素6大於基準值2,所以不用交換位置,high減1,繼續比較;

    第1.6步:此時low為1,high為1,此時代表第一次劃分排序完成,則將基準值放到這個位置;最終將原始資料分為左右兩部分,左部分只有一個元素1,不用再劃分了,右部分有6,5,9,3四個元素,繼續對於右部分進行劃分

    第2.1步:由於右部分是從索引位2開始,所以此時low為2,high為5,基準值為low為的元素6;

    第2.2步:由於剛開始選擇low位的元素為基準值(可以認為這個位置空了),接下來從high為開始遍歷比較,high為元素3小於基準元素6,需要將其放到基準元素的左邊,則將元素3放到low指向的位置。

    第2.3步:此時low為2,high為5,由於high位已經交換過了(可以認為這個位置空了),所以這次開始到low位進行遍歷比較,low位元素是剛交換過來的,所以不用交換位置;low加1繼續比較,此時low為3,對應位置的元素5小於6,不需要交換元素,則low加1,繼續比較;

    第2.4步:此時low為4,high為5,low位對應的元素9大於基準值6,所以需要將其放到基準元素的右邊,則將元素9放到high指向的位置。

    第2.3步:此時low為4,high為5,由於low位已經交換過了(可以認為這個位置空了),所以這次開始到high位進行遍歷比較,high位元素是剛交換過來的,所以不用交換位置;high減1繼續比較,此時high為4,此時low和high都為4,找到此次劃分基準值的位置,則將基準元素6的放到4位置; 到這又將原來的右部分6,5,9,3四個元素劃分為左右兩部分,右邊只有一個元素9,不用繼續劃分;左邊有元素3和5,繼續劃分排序;(這裡就不重複演示)

    最終通過遞迴劃分排序的方式,直到每個劃分部分內只有一個元素或空為止,即可獲得最後的排序結果。

1.3 快速排序演算法分析

時間複雜度

從上面解析步驟得知,每一次排序都是隻需要處理剩下未排序的元素,每一次排序時間複雜度不會超過O(n),但由於是通過遞迴進行劃分排序,所以快速排序的整體時間複雜度和遞迴層數有關,即總的時間複雜度為O(n*遞迴層數)

通過上面演示得知,其實最終將待排序資料劃分為一個二叉樹結構,在這二叉樹的高度就代表遞迴的層數(後續會專門分享這塊內容),如下圖:

image-20210506143932543

關於n個元素的二叉樹的最小高度為(log2n)+1,最大高度為n,如果待排序資料已經有序或逆序,如果每次都選擇每部分的首個元素為基準值,這樣就會導二叉樹高度增加,即遞迴深度就會增加;所以快速排序的時間複雜度最好為O(nlog2n),最壞為O(n2)

這樣以為快速排序就不行了嗎?當然不是,可以隨機選一個元素做為基準值,這樣不管待排序資料為有序還是逆序,都不會導致遞迴深度太深。所以最後快速排序的平均時間複雜度為O(nlog2n)

空間複雜度

空間複雜度在每次遞迴當中,用到的變數都是固定的(pivot,low,high),則最終影響空間複雜度的因素還是遞迴層數,則快速排序空間複雜度為O(遞迴層數), 最好為O(log2n),最壞為O(n)。

穩定性

由於是用待排序資料和基準值進行比較,所以最終元素交換位置不是固定的,則不能保證兩個相等元素原有順序不變,則快速排序是不穩定的。如下圖:

image-20210506150331140

如果取low位置的元素3作為基準值,最終會和元素2進行交換,最後就不能保證原來相等元素的前後順序了。

綜上所述,快速排序的時間複雜度為O(nlog2n),空間複雜度為O(log2n),是不穩定演算法;

總結

快速排序有效的解決了氣泡排序的缺陷,減少了比較次數,提升了排序效能。但當待排序列表為有序或逆序時,如果單純的取第一個元素或最後一個元素作為基準值,排序效能並沒有提升。所以實現排序演算法的關鍵是需要選個好的基準值,比如可以隨機選擇,也可以定義一個規則選擇,看小夥伴的實現方式咯。

感謝小夥伴的:點贊收藏評論,下期繼續~~~

一個被程式搞醜的帥小夥,關注"Code綜藝圈",跟我一起學~~~

相關文章