1 分治(Divide-and-Conquer)
分治的思想就是字面意义,“分而治之”。当我们拿到一个复杂问题时,如果不能够很好的解决,我们首先会想到的是将这个复杂的问题简单化。将原问题分成很多个子问题,再将子问题分成更小的子问题,直到他们是好解决能解决的。
分治法的基本思想:将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治的基本步骤:
- 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题
- 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
- 合并:将各个子问题的解合并为原问题的解
分治法的复杂性分析:分治时,将问题划分成很多子问题,这些子问题往往有很高的自相似,所以和递归的时间复杂度非常相似。其实递归就是实现很多分治算法的有效武器。用T(n)表示该分治法解规模为|P|=n的问题所需的计算时间: T(n)=kT(n/m)+f(n)
常见算法:二分搜索、合并排序、快速排序、汉诺塔问题。
2 Merge Sort 合并排序及其python实现
合并排序就是典型的采用分治算法来实现排序。按照之前讨论的分治的基本步骤,合并排序分为以下几个步骤:
分解:将需要排序的数组等分为左右两部分,直到不可再分割
解决:对分割好的子序列依次排序
合并:将排列好的子序列进行排序
以排列[ 1,3,4,6,8,9,5,2]的合并排序为例子:
将数组对半分组,分到最小后进行排序合并。合并排序的优势在于,每次合并时都是在合并已经排列好的数组,大大减少了排序的开销。
算法实现:
采用分治的有效武器,递归来实现算法
分解部分:我们只需要将需要解决的数组进行对半分,直到分解到不能再分割(数组的大小等于1)
合并部分:已知我们合并的数组都是已经排列好的数组,如何合并两个有序数组呢。我们只需要从数组的第一位进行比较,将小的放进合并的第一位,更大的是第二位,接着比较第二位,以此类推。
合并部分的思路如下:
while len(left)>0 and len(right)>0:
if left[0] < right[0]:
res.append(left.pop(0))
else:
res.append(right.pop(0))
if len(left)>0:
res.extend(left)
if len(right)>0:
res.extend(right)
合并算法代码实现:
#合并排序
def merge(left,right):
res=[]
while len(left)>0 and len(right)>0:
if left[0] < right[0]:
res.append(left.pop(0))
else:
res.append(right.pop(0))
if len(left)>0:
res.extend(left)
if len(right)>0:
res.extend(right)
return res
def merge_sort(arr:list):
if len(arr)<2:
return arr
mid=len(arr) //2
left=arr[:mid]
right=arr[mid:]
#不断将左右两部分再进行对半分
left_sort=merge_sort(left)
right_sort=merge_sort(right)
return merge(left_sort,right_sort)
时间复杂度分析:
可知在对left和right排序的过程中是遍历数组,时间复杂度是O(N)。在将数组对半分的时候(分成left和right),时间复杂度时O(logN),类比二分法可知:
归并排序的时间复杂度为O(nlogn),其执行效率与要排序的原始数组的有序程度无关。