分治策略
分治法步骤
分治法在每一层递归上都有三个步骤:
分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;
合并:将各个子问题的解合并为原问题的解。
分治法:可以看作是二叉树的递归
-
全排列
问题:计算从1,2,…,n的n个数的全排列
思路:分解:首先从1至n中依次选出一个数,然后对剩余的n-1个数再依次选出一个数,重复上述过程,直到只剩下一个数时,递归结束,这是分的过程。
合并:对拆分的结果依次跟选出来的某个数进行合并,即可得到一个排列结果。
def permute(arr): if len(arr)==1: return [arr] res = [] for i in range(len(arr)): num = arr[i] rest = arr[:i]+arr[i+1:] for rest_permute in permute(rest): res.append([num]+rest_permute) return res arr = list(range(1,4)) res = permute(arr) for i in res: print(i)
运行结果:
[1, 2, 3] [1, 3, 2] [2, 1, 3] [2, 3, 1] [3, 1, 2] [3, 2, 1]
-
归并排序
思路:可以看作是二叉树的后根遍历
终止条件:当数组中只有一个元素时,遍历终止,并返回数组本身
如果数组的长度大于1,则从中间将数组拆分成左右两个子数组,可看作是左右子树,拆分到数组的长度小于等于1为止。
然后对拆分后的左右两个子数组合并,合并时小数字在前,大数字在后
分解:将数组依次从中间分成左右两半,直到分解成单个元素为止
合并:依次将分解后的两个数组按照大小合并,最终得到的就是排序后的结果
def merge(arr1, arr2): i, j = 0, 0 res = [] while i<len(arr1) and j<len(arr2): if arr1[i]<arr2[j]: res.append(arr1[i]) i+=1 else: res.append(arr2[j]) j+=1 if i<len(arr1): res.extend(arr1[i:]) if j<len(arr2): res.extend(arr2[j:]) return res def merge_sort(arr): if len(arr)<=1: return arr l = 0 r = len(arr) m = (l+r)//2 l_arr = merge_sort(arr[l:m]) r_arr = merge_sort(arr[m:r]) res = merge(l_arr, r_arr) return res import numpy arr = numpy.random.randint(0,20,8) arr = list(arr) # arr = [3,6,2,4,7] print(arr) arr_sort = merge_sort(arr) print(arr_sort)
-
多数元素
思路:卡伊看作是二叉树的后根遍历
递归的结束条件:当数组中只有一个元素的时候,返回该元素
如果数组的长度大于1, 将数组从中间分成左右两个子数组(即左右子树)
先调用左子树,再调用右子树
最后是根节点,当左右子树的多数元素相同时返回,否则分别统计左右子树的多数元素在整个数组中出现的次数,出现次数最多的就是最终得到的多数元素。
class Solution: def majorityElement(self, nums: List[int]) -> int: def helper(nums, l, r): if l==r: return nums[l] mid = (l+r)//2 left = helper(nums, l, mid) right = helper(nums, mid+1, r) if left == right: return left left_count = sum([1 for i in range(l,r+1) if nums[i]==left]) right_count = sum([1 for i in range(l, r+1) if nums[i]==right]) return left if left_count>right_count else right return helper(nums, 0, len(nums)-1)