算法策略
贪心算法
一个贪心算法总是做出当前最好的选择,也就是说,它期望通过局部最优选择从而得到全局最优的解决方案。
- 做出选择 不可反悔
- 可能得不到最优解
- 贪心策略决定算法好坏
确定贪心策略 选择当前看上去最好的方案
一步一步得到局部最优解
将所有局部最优解合并称为一个原来问题的最优解
- 冒泡排序就使用了贪心算法
原则
贪心原则
- 所谓贪心选择性质是指原问题的整体最优解可以通过一系列局部最优的选择得到
最优子结构
- 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质
背包问题
选择一个最好的贪心策略
物品可分割称为背包问题 物品不可分割称为0-1背包问题
- 会议安排问题
- 最短路径问题
- 哈夫曼编码
- 最小生成树
递推法
由当前问题的逐步解决从而得到整个问题的解,只是依赖的是信息间本身的递推关系
递归法
解规模为N的问题,设法将它分解成规模较小的问题,然后从这些小问题的解方便地构造出大问题的解,并且这些规模较小的问题也能采用同样的分解和综合方法
特别地,当规模N=1时,能直接得解
枚举法
对所有可能的解逐一尝试,从而找出问题的真正解
这就要求所解的问题可能的解是有限的、固定的,不会产生组合爆炸、容易枚举的
分治法
其本质就是将一个大规模的问题分解为若干个规模较小的相同子问题,分而治之, 递归地求解问题
每层递归应用:
- 分解当前问题为子问题
- 递归解决分解的子问题
- 合并子问题的解
对于递归的问题,当不需要再继续递归分解子问题时,就是一个基本情况,此时可直接得出解,否则就是递归情况
递归式则可以用来描述复杂度,如归并排序的递归式
O(1) 若 n = 1
2T(n/2) + O(n) 若 n > 1
分支算法要素
- 原问题可以分解为若干个规模较小的相同子问题
- 子问题相互独立
- 子问题解可以合并为原问题的解
- 二分查找
- 归并排序
- 快速排序
- 大整数乘法
动态规划
通过子问题的最优解,推导出最终问题的最优解,把这些子问题之间的转移称为状态转移,并把用于刻画这些状态转移的表达式称为状态转移方程
基础
动态规划的思想类似于分治法
不过动态规划是从最小子问题求起 将小问题的解存储起来
求解大问题时 如果需要用到小问题的解 直接使用即可 无需再重复计算
- 待备忘的自顶向下法:当对一个子问题进行求解时,会判断这个子问题是否已经求过解,如果是直接返回,否则进行计算并放入缓存
- 自底向上法:求解一个问题时,其依赖的子问题都已经被求解完毕
要素
- 最优子结构
- 问题的最优解包含其子问题的最优解
- 子问题重叠
- 不是必要条件 但是是动态规划的优势
- 兔子序列
- 最长公共子串
回溯法
回溯法是一种选优搜索法,按照选优条件深度优先搜索,以达到目标。当搜索到某一步时,发现原先选择并不是最优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术称为回溯法,而满足回溯条件的某个状态称为“回溯点”。
要素
- 解空间
- 由所有可能解组成的空间
- 将这些解按一定结构组织起来:解空间树
- 隐约束
- 不满足隐元素的分支无需搜索 直接剪掉 也称为剪枝函数
- 0-1背包
- 最大图
- 地图着色
- n皇后
分支限界法
- 广度优先
回溯法找出所有解
分支限界法找出一个接
回溯法深度优先 分支限界法广度优先
回溯法搜索一次生成一个孩子节点 分支限界法一次生成所有节点
线性规划网络流
解决
- 确定决策变量
- 哪些变量对模板有影响
- 确定目标函数
- 含有决策变量的线性函数
- 找出约束条件
- 将对决策变量的约束表示为方程或者不等式
- 求最优解
算法思想
- 灵光一现的算法,通常比较独特且不易想到,但却能以非常简洁的代码实现并且达到出色的性能表现
数据结构决定程序
使用更合适的数据结构能减少代码量
正确的程序
- 断言
- 测试
性能分析
- 问题定义
- 系统结构
- 算法与数据结构
- 代码调优
- 系统软件
- 硬件
估算
使用小实验获取关键参数
任何事都应尽量简单 但不宜过于简单
算法设计
- 保存状态 避免重复计算
- 动态规划
- 对信息进行预处理至数据结构中
- 分治算法
- 扫描算法
- 还是动态规划
代码调优
- 不成熟的优化是大量编程灾害的根源
- 需要有一个度量工具
- 注意代码调优是一把双刃剑
一些优化方案:
- 缓存
- 等价的代数表达式
- 内联函数避免函数调用开销
- 循环展开
- 修改数据结构
节省空间
- 调整数据结构
- 使用计算代替存储
- 稀疏数据结构
- 关键字索引
- 数据压缩
- 动态废品
- 垃圾回收
自描述数据
- KV对
- 注释文档
- 源代码描述
技巧
- 明白用户的真实需求
- 评估成本与收益
- 正确评估问题的难度
- 正确的工具