分治策略
1. 分治策略
分治策略本质上就是分和治,分就是将一个大问题分成多个小问题去解,治就是利用多个小问题的解来得出一个大问题的解,分治策略大体上有以下三个步骤:
- 分解(Divide):将问题划分为一些子问题,子问题的形式与原问题完全一样,只是规模更小
- 解决(Conquer):当问题规模足够小时,停止递归,求解出当前子问题
- 合并(combine/merge):将子问题的解组合成原问题的解
分治策略的核心是递归,分治的本质是自己分解自己和自己合并自己,递归的本质是自己调用自己并自己处理自己的返回值,递归过程中存在以下两种情况:
- 递归情况(recursive):子问题足够大时,无法求解或者很难求解,需要将问题分解为子问题
- 基本情况(base):子问题足够小时,无法分解或者没必要分解,直接求解子问题
1 | 分解: |
递归式:
- T(n):是当前问题解决所需要的时间
- a:是当前问题分成的子问题的个数
- b:是当前问题分成的子问题的规模
- f(n):是合并子问题的解所需要的时间
2. 归并排序
归并排序,根据分治策略分为以下三步:
- 分解:可以从中间将当前数组分为两个子数组递归
- 解决:如果当前数组长度为1,无法继续分,也可以认为已经排好序,停止递归,返回该数组
- 合并:对两个已经排好序的数组,设立两个指针进行遍历,可以很轻松地合并为一个排好序的数组
递归式:
1 | def merge(left,right): |
3. 找到最大子数组
最大子数组指的是子数组的值相加最大,根据分治策略分为以下三步:
- 分解:可以从中间将当前数组分为左子数组和右子数组递归,此外还有一种情况是跨域中间的子数组
- 解决:如果当前数组的长度是1,直接返回值,此外还需要计算跨域中间的子数组的最大值
- 合并:选取左子数组、右子数组和跨越中间的子数组的最大值
这道题的特殊性在于:有一种情况是不能递归分解的,需要在当下直接解决
递归式:
1 | def merge(sum1,sum2,sum3): |
4. 矩阵乘法的Strassn算法
这里要求两个矩阵都是nxn矩阵,而且n都是2的n次幂,即保证n/2是整数
Strassen算法的基本步骤如下:
- 分解矩阵:
- 计算中间值
- 组合结果
核心:减少了一次矩阵乘法,增加常数次矩阵加法,以此实现更小的时间复杂度,根据分治策略分为以下三步:
- 分解:将每个大矩阵分成四个角的子矩阵
- 解决:如果两个矩阵是1x1的,则直接求矩阵成绩
- 合并:按照Stassen算法将7个解组合成四个角的子矩阵,最后组合成一个结果矩阵
1 | import numpy as np |
5. 二分查找
根据分治策略分为以下三步:
- 分解:将数组从中间分为左子数组和右子数组
- 解决:如果当前数组的长度是1,直接返回数组仅有的元素值
- 合并:取左子数组的最大值和右子数组的最大值的较大值返回
1 | def merge(num1,num2): |
6. 数列的逆序对
根据分治策略分为以下三步:
- 分解:将数组从中间分为左子数组和右子数组
- 解决:如果当前数组的长度为1,则返回0,此外还需要计算左数组相较于右数组的逆序对数
- 合并:将左子数组的逆序对、右子数组的逆序对、左数组相较于右数组的逆序对数相加
1 | def merge(num1,num2,num3): |
