分治算法(D&C)
分治算法是一種思想,他把一個大問題縮小爲規模比較小的問題,小問題得到解決之後,大問題也隨之解決。分治算法通常利用遞歸來實現。利用遞歸實現的步驟如下:
- 明確遞歸函數的返回值;
- 尋找基線條件,這種條件務必簡單;
- 實現遞歸體:不斷將問題分解(縮小問題規模),直到符合基線條件;
例如,我們有一塊土地,尺寸爲 ,現要求把這塊土地劃分爲均勻的方塊且要求儘可能的大,如何進行劃分呢?
我們利用分治思想來解決這個問題,分治算法的核心在於縮小問題的規模,的方塊可以劃分爲三個方塊,其中,是在現有基礎之上能夠劃分的最大方塊。圖示如下所示:
我們的問題現在縮小到在方塊 中尋找最大切分方塊。餘下的操作以此類推…。那何時停止呢?直到方塊縮小到兩邊長成倍數關係,即可停止。最終的切分圖如下所示:
利用分治算法解決數據累加
假設要用分治算法實現列表: 的累加和。我們定義一個遞歸函數,用於計算列表的累加和;首先要明確 D&C 的兩個問題:
- 基線條件:當列表只有一個元素時,返回元素值;
- 遞歸體:縮小列表規模:
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
利用分治算法找出列表中的最大數**
- 基線條件:列表元素只有一個;
- 遞歸條件:縮小問題規模,計算剩下列表中最大數;
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
利用分治算法實現二分查找
- 基線條件:找到標籤或沒有標籤;
- 遞歸體:見代碼
# 列表不作爲參數,可以提高遞歸的執行效率
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。快速排序的思想流程如下:假設要對列表 進行排序,如下所示:
首先選擇一個基準值,比基準值小的數據放在基準值的左邊,比基準值大的數據放在基準值的右邊,如下所示:
然後對左區列表和右區列表執行上述相同的操作。其代碼如下:
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)
快速排序的時間複雜度
快速排序的平均時間複雜度是:;在最糟糕情況下時間複雜度爲:。
時間複雜度爲 的情況,此時基準值取的列表的中間值:
當對一個有序數列進行快排且基準值取第一個值時,時間複雜度會是,如下圖所示:
遞歸調用的層數如下:
總結
- D&C將問題縮小爲更小規模進行解決。使用D&C處理列表時,基線條件是空數組或單元素數組;
- 實現快排時,請隨機地選擇基準值的元素。快排的平均運行時間爲;
- 大O表示法中的常量有時候事關重大;