1. 程式人生 > >演算法快學筆記(四):快速排序的原理與實現

演算法快學筆記(四):快速排序的原理與實現

1. 原理介紹

快速排序是一種排序演算法,速度比選擇排序快得多,其主要基於“分而治之”的思想對集合進行排序,本文將對該演算法進行分析。

2. 分而治之(D&C)的思想

D&C主要指利用遞迴的方式來不斷的縮小需要處理問題的規模,最終使問題容易解決。使用D&C解決問題的過程包括兩個步驟。
(1) 找出遞迴的終止條件,這種條件必須儘可能簡單(稱為基線
條件)。
(2) 不斷將問題分解(或者說縮小規模),直到符合遞迴的終止條件(稱為歸納條件)。

為了便於理解,舉個例子。

假設有一個數組A=[2,4,6], 想要統計A所有元素的和,可以使用兩種方式

  1. 遍歷A,並將結果一個一個的加起來
  2. 使用D&C思想,通過不斷縮小集合的規模,最終把每次遞迴的結果加起來,流程如下

在這裡插入圖片描述
程式碼如下:

// 使用dc的思想統計一個數組元素的和
func Sum(ints [] int) int {
	if len(ints)==1{
		//當集合只有一個元素時,返回結果
		return ints[0]
	}
	subArr := ints[1:]
	//每次遞迴的時候,傳個Sum的集合數量都比上一次少一個元素
	return ints[0]+Sum(subArr)
}

3. 快速排序的原理

快速排序的流程大概分為兩步:

  1. 確定遞迴的終止條件:對於排序操作而言,如果陣列為空或只包含一個元素,則只需原樣返回陣列且不用排序。因此陣列為空或只包含一個元素可以作為迴圈結束的條件

  2. 如果沒有滿足遞迴終止條件,需要將陣列分解,並做以下操作,直到滿足遞迴終止條件。

    2.1. 從陣列中選擇一個元素,這個元素被稱為基準值

    2.2. 將陣列分成兩個子陣列:小於基準值的元素和大於基準值的元素

    2.3. 對步驟2產生的兩個子陣列再執行快速排序操作

假設要對[2,1,3,5,4]進行排序,選擇了3為基準值,則流程大概如下:
在這裡插入圖片描述

對於快速排序,在基線條件中,我證明這種演算法對空陣列或包含一個
元素的陣列管用。在歸納條件中,如果快速排序對包含一個元素的陣列管用,對包含兩個元素的陣列也將管用;如果它對包含兩個元素的陣列管用,對包含三個元素的陣列也將管用,以此類推。因此,可以說明快速排序對任何長度的陣列都管用。

4. 執行時間分析

快速排序的平均情況執行的時間複雜度為O(n log n),但在最糟糕的情況下其複雜度將為O(n^2),下面對兩種情況進行分析

4.1 最糟糕情況

下面來看看到底啥時候才會出現最糟糕的情況,考慮如下的排序:
在這裡插入圖片描述

快速排序的效能高度依賴於你選擇的基準值。假設你總是將第一個元素用作基準值,且要處
理的陣列是有序的。由於快速排序演算法不檢查輸入陣列是否有序,因此它依然嘗試對其進行排序,需要進行N層操作,每層需要比較N個數。所以演算法複雜度為O(n^2)

4.2 最佳情況

考慮如下排序:
在這裡插入圖片描述

該圖也是對有序陣列進行排序,但每次都選擇中間的元素作為基準值,此時遞迴的次數變成了3次(logN),每次也是需要比較N個元素,所以演算法複雜度為O(n log n)

5. 程式碼實現

為了簡單演示,程式碼都始終選用第一個元素作為基準值

Python版本:

def quicksort(array):
    if len(array) < 2:
        return array
    else:
        pivot = array[0]
        less = [i for i in array[1:] if i <= pivot]
        greater = [i for i in array[1:] if i > pivot]
        return quicksort(less) + [pivot] + quicksort(greater)
print quicksort([3,5,6,2,6,2])

總結

  • D&C將問題逐步分解。使用D&C處理列表時,基線條件很可能是空陣列或只包含一個元
    素的陣列。
  • 快速排序的平均執行時間為O(n log n)。
  • 當陣列有序,始終選擇第一元素作為基準值時,快速排序將出現最糟情況
  • 當陣列有序,始終選擇中間元素作為基準值時,快速排序將出現最佳情況