分而治之思想
使用D&C解决问题的过程包括两个步骤:
1.找出基线条件,这种条件必须尽可能简单;
2.不断将问题分解(缩小问题规模),直到符合基线条件。
举例:给定一个数字数组,将这些数字相加返回结果。
使用循环方法:
def sum(arr):
total = 0
for x in arr:
total +=x
return total
print sum([1,2,3,4])
如何使用递归函数方法呢??
第一步:找出基线条件。最简单的数组什么样呢?–数组中不包含元素,或只含有一个元素,求和最容易。
第二步:每次递归调用使其离空数组更近一步,,缩小问题规模。
sum([2,4,6])===2 + sum([4,6])
描述:对于一个列表。若列表为空,就返回0;否则,计算列表中第一个元素与除去第一个元素的其他数字的求和。
Tips:涉及数组的递归函数时,基线条件通常是数组为空或只包含一个元素
#数组元素求和
def sum(arr):
if(arr == []):
return 0
else:
return arr[0]+sum(arr[1:])
print(sum([1,2,3,4,5,6,7,8,9,10]))
递归函数计算列表中包含的元素数
#列表中元素的个数
def count(arr):
if (arr == []):
return 0
else:
return 1+count(arr[1:])
print(count([1,2,3,4,5,6,7]))
找出列表中最大的数字(这一个开始没有想到,所谓最大是在一次次地比较中得出的)
#第一个元素与后面元素比较,选出最大的
def maxNum(arr):
if len(arr)==2:#最简单时候,只有两个元素,比较一下选出大的数
if(arr[0]>arr[1]):
return arr[0]
else:
return arr[1]
else:#第一个元素与后面元素的整体进行比较,从而缩小问题的规模
if(arr[0]>maxNum(arr[1:len(arr)])):
return arr[0]
else:
return maxNum(arr[1:len(arr)])
print(maxNum([1,2,3,4,5,6,8]))
最后让我们再仔细回想二分查找中包含的递归思想以及涉及的条件
def search(list,item):
low = 0
high = len(list) - 1
#最简单的情况,数组中只包含一个元素,若要查找的值与这个元素相同就找到了
while(low<=high):
mid=int((low+high)/2)
if(list[mid]<item):#舍弃左边一半,对右半部分进行二分查找,即缩小了问题规模
low = mid + 1
elif(list[mid]>item):
high = mid - 1
else:
return mid
return None
my_list = [1,2,3,4]
print (search(my_list, 1))
print (search(my_list, 2))
二分查找的递归条件中,把数组分为两半,将其中一半丢弃,并对另一半执行二分查找。
快速排序也使用了D&C
对排序算法来说,最简单的数组为空或只包含一个元素,在这时,根本不用排序
排序的原理:
在数组中选择一个元素作为基准值,接着找到比基准值小的元素和比其大的元素进行分区。
part1:小于基准值的数字的子数组;
part2:基准值
part3:大于基准值的数字的子数组;
对子数组进行快速排序,再合并结果,可得有序数组
包含三个元素的数组可以进行排序,在此基础上递归地快速排序,经过归纳证明,以此类推,快速排序对任何长度的数组都管用。
#快速排序
def quicksort(arr):
if len(arr)< 2:#基准条件:空或只含两个元素的数组是有序的
return arr
else:
pivot = arr[0]#递归条件
left = [x for x in arr[1:] if x <= pivot]#小于基准值构成的子数组
right = [x for x in arr[1:] if x > pivot]#大于基准值构成的子数组
return quicksort(left)+[pivot]+quicksort(right)
print(quicksort([10,5,2,3]))
快速排序在以基准值进行划分时,在调用栈的每层都涉及O(n)个元素
最佳情况(平均情况):O(nlogn)
最糟情况:o(n^2)
只要每次都随机地选择一个数组元素作为基准值,快速排序的平均运行时间就为O(nlogn).快速排序是最快的排序算法之一,也是D&C典范。