1. 程式人生 > >排序演算法:快速排序

排序演算法:快速排序

概述

手寫排序演算法幾乎是程式設計師面試必問的題目,大多數人都會選擇寫氣泡排序,如果此時你寫的是其他改進過的排序演算法,相信會讓面試官眼前一亮。本文將介紹常見的排序演算法中的“快速排序”。

基本思想

快速排序(QuickSort)是對氣泡排序的一種改進。快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:

  1. 從要排序的資料中取一個數為“基準數”。
  2. 通過一趟排序將要排序的資料分割成獨立的兩部分,其中左邊的資料都比“基準數”小,右邊的資料都比“基準數”大。
  3. 然後再按步驟2對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列。

該思想可以概括為:挖坑填數 + 分治法。

分治法

分治,字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合併。在電腦科學中,分治法就是運用分治思想的一種很重要的演算法。分治法是很多高效演算法的基礎,如快速排序,歸併排序,傅立葉變換(快速傅立葉變換)等等。

例子

下面通過一個例子來看看快速排序是怎麼工作的,例子中表格中紅色的字型為需要填的坑,綠色的字型為已經移動過的資料。

1.剛開始,i 和 j 分別指向陣列頭和陣列尾,即 i = 0,j = 9,基準數取第一個數,即index = array[i] = array[0] = 23。

此時,array[0]的值已經存在了index,因此array[0]的位置就好像被挖了個坑,可以填充一個數。

因此,我們從位置 j 開始向左尋找比index小的數,當 j = 8 時,符合條件,於是我們將array[8]的值填到array[0] ,即將9填入array[0],並將 i 向右移動一個位置,即 i++。從位置 j 向左尋找比index小的數,並在尋找到後填入坑中,用程式碼表示如下。

此時,array陣列如下圖。

2.因為array[0]的坑被array[8]填了,於是array[8]的位置又成了一個新的坑。此時我們從位置 i 開始向右尋找比index大的數,當 i = 2 時符合條件,於是我們將array[2]的值填到array[8] ,即將37填入array[8],並將 j 向左移動一個位置,即 j--。從位置 i 向右尋找比index大的數,並在尋找到後填入坑中,用程式碼表示如下(跟上面相似)。

此時,array陣列如下圖。

3.同樣的,array[8]的坑被array[2]填了,於是array[2]的位置又成了一個新的坑。此時我們從位置 j 開始向左尋找比index小的數,當 j = 5 時符合條件,於是我們將array[5]的值填到array[2] ,即將21填入array[2],並將 i 向右移動一個位置,即 i++,此時array陣列如下圖。

4.同樣的,array[2]的坑被array[5]填了,於是array[5]的位置又成了一個新的坑。此時我們從位置 i 開始向右尋找比index大的數,當 i = 3 時符合條件,於是我們將array[3]的值填到array[5] ,即將89填入array[5],並將 j 向左移動一個位置,即 j--,此時array陣列如下圖。

5.同樣的,array[5]的坑被array[3]填了,於是array[3]的位置又成了一個新的坑。此時我們從位置 j 開始向左尋找比index小的數,當 j = 4 時符合條件,於是我們將array[4]的值填到array[3] ,即將2填入array[3],並將 i 向右移動一個位置,即 i++,此時array陣列如下圖。

6.此時,我們發現 i = j,結束遍歷,並將index填入array[4],即將23填入array[4],此時array陣列如下圖。此時,array[4]左邊的資料全比array[4]小,而array[4]右邊的資料全比array[4]大。

7.接下去,我們只需要對array[4]兩邊的資料分別在進行上面的操作即可(分治法),如下圖。

分治的程式碼可以寫成如下:

quickSort(array, low, i - 1); // 遞迴呼叫,分治
quickSort(array, i + 1, high);     // 遞迴呼叫,分治

完整流程

最後我們通過一個視訊(找不到動圖,僵硬)來看下快速排序的完整過程。視訊中的紅線為當前的基準數。CSDN放不了視訊,想看視訊的關注公眾號,同名文章裡有。

整合

根據以上步驟,稍微整理一下,可以得出快速排序的程式碼如下:

時間複雜度

最好情況的時間複雜度為O(nlogn),過程比較複雜,就不在此贅述。

最差情況下每一次取到的數(基準數)都是當前要比較的數中的最大/最小值,在這種情況下,每次都只能得到比上一次少1個數的子序列(即要麼全比基準數大,要麼全比基準小),此時相當於一個氣泡排序,比較的次數 = (n - 1) + (n - 2) + ... + 2 + 1 = (n - 1) * n / 2,此時的時間複雜度為:O(n^2)。最差情況一般出現在:待排序的資料本身已經是正序或反序排好了。

使用場景

基本上在任何需要排序的場景都可以使用快速排序。雖然快速排序的最壞情況時間複雜度為O(n^2),但是由於基本不會出現,因此可以放心的使用快速排序。在本人的電腦測試,100萬的隨機數字,快速排序大約耗時120毫秒。

最後

理解了快速排序的基本思想後,手寫快速排序演算法就變得沒那麼難了,只需要多練習幾遍,相信在面試中手寫快速排序演算法便是小菜一碟。