常见算法总结
类别归类:蛮力,分治,动态规划,贪心,回溯,分支限界。
实现方式有两种,一般为基于迭代和基于递归。
一.蛮力Brute-force
没有什么好说,就是遍历或者枚举。
二.分治
分而治之,将大问题拆解成相互独立且相似的子问题。
步骤:1、先分 2 求解 3 .合并
实现方式:一般递归实现
三.动态规划
和暴力相比,dp利用了子问题间的依赖关系,减少了一些计算
适用条件:1.满足最优子结构性质,即最优解所包含的子问题的解也是最优的
构造递推式:
0.确定维度,一般一维或者二维
1.注意含义
2.选择分隔点
a. 一般和前一步或者两步有关,比如最大子序和,$dp[i]=max(s[i],s[i]+dp[i-1])$
b. 但有时候需要遍历全部分割点,比如单词拆分,$dp[i]=dp[j] \ \& \& \ check(s[j..i−1])$,
c. 有时候动态选择分隔点,比如丑数,$ dp[i]=min(dp[p2]2,dp[p3]3,dp[p5]*5) $,其中$p2,p3,p5$动态变化
3.注意+, -
一定是用已有的递推没有的
举例比如62. 不同路径,$dp[i][j] = dp[i-1][j] + dp[i][j-1]$;
1 | class Solution: |
比如5. 最长回文子串,$dp[i][j]=dp[i+1][j-1] \ and \ s[i]==s[j]$
s = “babad”
n=5
(i,j)
(0,1) (1,2) (2,3) (3,4) (0,2) (1,3) (2,4) (0,3)
1 | class Solution: |
解的追踪:有时候需要借助备忘录搜索解,时间复杂度不超过计算备忘录
实现方式
递归实现:效率不高的原因在于子问题重复计算了,比起暴力哪个快?应该还是这个
迭代+备忘录:不一定全部都要存储,需要的存着了就可以了,每次子问题计算一次(空间换时间)
一般采用迭代+备忘录
状态压缩动态规划
https://jimmy-shen.medium.com/bitmask-state-compression-dp-39e7ba56978b
https://segmentfault.com/a/1190000038223084
https://blog.csdn.net/wxgaws/article/details/114693162
Usually, the state of DP can use limited variables to represent such a dp[i] , dp[i] [j] ,dp[i] [j] [k].
However, sometimes, the states of a DP problem may contain multiple statuses. In this case, we can think about using the state compression approaches to represent the DP state.
说白了就是在状态很多的时候对状态降维
目的:可以优化空间复杂度,不知道可以不可以优化时间复杂度
四.贪心
短视,局部最优(少考虑很多,计算量就下去了)
需要证明
五.回溯
一般用于可以用树型结构建模的问题
回溯法能系统地搜索一个问题的所有解,和暴力的区别在于回溯可以剪枝,以此减少计算量
实现方式:利用DFS搜索解空间,一般递归实现
https://zhuanlan.zhihu.com/p/93530380
https://zhuanlan.zhihu.com/p/112926891
六.分支限界
类似回溯,区别如下
代价函数
以极大值问题为例,以当前结点为根的子树能够得到的可行解的最大值
界
以极大值问题为例,当前已经得到的可行解的最大值
减枝
代价函数和界可以用于减枝,以极大值问题为例,界大于等于某结点代价函数,该结点就不需要继续搜索了
参考文献
https://blog.csdn.net/zm1_1zm/article/details/69224626
https://blog.csdn.net/wxgaws/article/details/114693162
https://segmentfault.com/a/1190000038223084
https://jimmy-shen.medium.com/bitmask-state-compression-dp-39e7ba56978b