分治算法與快排

分治算法(D&C)


分治算法是一種思想,他把一個大問題縮小爲規模比較小的問題,小問題得到解決之後,大問題也隨之解決。分治算法通常利用遞歸來實現。利用遞歸實現的步驟如下:

  1. 明確遞歸函數的返回值;
  2. 尋找基線條件,這種條件務必簡單;
  3. 實現遞歸體:不斷將問題分解(縮小問題規模),直到符合基線條件;

例如,我們有一塊土地,尺寸爲 1680640m21680*640m^2,現要求把這塊土地劃分爲均勻的方塊且要求儘可能的大,如何進行劃分呢?

在這裏插入圖片描述

我們利用分治思想來解決這個問題,分治算法的核心在於縮小問題的規模,1680640m21680*640m^2的方塊可以劃分爲640640m2640640m2640400m2640*640m^2,640*640m^2,640*400m^2三個方塊,其中,640640m2640*640m^2是在現有基礎之上能夠劃分的最大方塊。圖示如下所示:

在這裏插入圖片描述

我們的問題現在縮小到在方塊 640400m2640*400m^2 中尋找最大切分方塊。餘下的操作以此類推…。那何時停止呢?直到方塊縮小到兩邊長成倍數關係,即可停止。最終的切分圖如下所示:

在這裏插入圖片描述
在這裏插入圖片描述

經過分治算法的處理可以得到最大的切分方塊爲 $80*80$。

利用分治算法解決數據累加


假設要用分治算法實現列表:[1,2,3,4][1, 2, 3, 4] 的累加和。我們定義一個遞歸函數,用於計算列表的累加和;首先要明確 D&C 的兩個問題:

  1. 基線條件:當列表只有一個元素時,返回元素值;
  2. 遞歸體:縮小列表規模:
def addlist(arr):
    if len(arr) == 1: # 定義基線條件
        return arr[0]
    elif len(arr) == 0:
        return 0
    else: # 定義遞歸體
        return arr[0]+addlist(arr[1:])
        
print(addlist([1, 2, 3, 4])) # 10

利用分治算法找出列表中的最大數**


  1. 基線條件:列表元素只有一個;
  2. 遞歸條件:縮小問題規模,計算剩下列表中最大數;
def calListMax(arr):
    if len(arr) == 0:
        return 0

    if len(arr) == 1: # 基線條件
        return arr[0]

    value = calListMax(arr[1:])
    if arr[0] <= value:
        return value;
    else:
        return arr[0];

print(calListMax([2, 9, 2, 5, 23, 62, 34])) # 62

利用分治算法實現二分查找


  1. 基線條件:找到標籤或沒有標籤;
  2. 遞歸體:見代碼
# 列表不作爲參數,可以提高遞歸的執行效率
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

def binary_sort(lowIndex, hightIndex, value):
    if len(arr) == 0:
        return -1

    if lowIndex > hightIndex:
        return -1
    else:
        mid = (lowIndex+hightIndex)//2;
        if value == arr[mid]:
            return mid;
        elif value < arr[mid]:
            hightIndex = mid-1
        else:
            lowIndex = mid+1

        return binary_sort(lowIndex, hightIndex, value)

print(binary_sort(0, len(arr)-1, 8)) # 7

快速排序


快速排序是一種常用的排序算法,比選擇排序快得多。例如, C語言標準庫中的函數 qsort 實現的就是快速排序。快速排序也使用了D&C。快速排序的思想流程如下:假設要對列表 [33,15,10][33, 15, 10] 進行排序,如下所示:

在這裏插入圖片描述

首先選擇一個基準值,比基準值小的數據放在基準值的左邊,比基準值大的數據放在基準值的右邊,如下所示:

在這裏插入圖片描述

然後對左區列表和右區列表執行上述相同的操作。其代碼如下:

def quick_sort(arr):
    if len(arr) <= 1: # 基線條件
        return arr[0]
    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)    

快速排序的時間複雜度

快速排序的平均時間複雜度是:O(nlogn)O(nlogn);在最糟糕情況下時間複雜度爲:O(n2)O(n^2)
時間複雜度爲 O(nlogn)O(nlogn) 的情況,此時基準值取的列表的中間值:

在這裏插入圖片描述

當對一個有序數列進行快排且基準值取第一個值時,時間複雜度會是O(n2)O(n^2),如下圖所示:

在這裏插入圖片描述

遞歸調用的層數如下:

在這裏插入圖片描述


總結


  1. D&C將問題縮小爲更小規模進行解決。使用D&C處理列表時,基線條件是空數組或單元素數組;
  2. 實現快排時,請隨機地選擇基準值的元素。快排的平均運行時間爲O(nlogn)O(nlogn)
  3. 大O表示法中的常量有時候事關重大;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章