复杂度的大O表示法
- 对于给定的函数g(n),用O(g(n))来表示以下函数的集合:
- 算法分析中,一般用大O符号来表示函数的
渐进上界
; - 表示,当数据量达到一定程度时候,g(n)的增长速度不会超过O(g(n))限定的范围;
常见的算法复杂度
- O(1)、O(log(n))、O(√n)、O(n)、O(nlog(n))、O(n^2);
空间复杂度
- 算法执行所占用的空间:
- Array[100]:O(100);
- Array[N]: O(N);
- 有时候递归调用,需要计算调用栈的空间;
算法的分类
按照应用目的
- 搜索算法;
- 排序算法;
- 字符串算法;
- 图算法;
- 最优化算法;
- 数学算法;
按照实现策略
- 暴力法;
- 增量法
- 分治法;
- 贪心算法;
- 动态规划法;
- 回溯法;
- 分支限界法;
分治算法
- 分而治之
- 问题的规模越小,越容易解决;
- 把复杂问题不断分成多个相同或相似的子问题,直到每个子问题可以简单地进行求解;
- 将所有子问题的解合并起来,就是原问题的解;
- 分治和递归
- 产生的子问题形式往往和原问题相同,只是原问题的较小规模表达;
- 使用递归手段求解子问题,可以很容易地将子问题的解合并,得到原问题的解;
基本步骤
- step1:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
- step2:若子问题规模较小而容易被解决则直接解,否则递归地解决各个子问题;
- step3:将各个子问题的解合并为原问题的解;
应用场景:
- 二分搜索、大整数乘法、归并排序、快速排序;
- 棋盘覆盖问题、循环赛日程表问题、汉诺塔问题等;
贪心算法
- 贪心(Greedy):总是做出在当前看来是最好的选择
- 从不整体考虑,只考虑眼前,得到
局部最优解
;
- 从不整体考虑,只考虑眼前,得到
- 局部最优解和全局最优解
- 要保证最终得到的是全局最优解,贪心策略必须具备
无后效性
;
- 要保证最终得到的是全局最优解,贪心策略必须具备
- 适用场景:
- 用贪心算法直接求解全局最优,条件苛刻;
- 哈夫曼(Huffman)编码;
- 具体实现框架:
动态规划
- 动态决策的过程
- 把原问题划分为多个"阶段",依次来做"决策",得到当前的局部解;
- 每次决策依赖于当前的"状态",随即引起状态的转移;
- 一个决策序列就是在变化的状态中产生出来的;
- 这种多阶段决策最优化,解决问题的过程就称为
动态规划
;
- 最优化问题
- 动态规划通常用来求解最优化问题;
- 可以有很多可行解,每个解都对应一个值,希望找到具有最优值的解;
回溯法
- 选优搜索法
- 按照一定的选优条件,不停向前搜索,直到达到目标;
- 如果搜索到某一步没发现之前的选择并不优,就退回一步重新选择;
- 深度优先搜索(DFS)策略
- 在包含问题所有解的解空间树,按照深度优先搜索的策略,从根节点触发、深度搜索解空间树;
- 回溯法就是对隐式图的深度优先搜索算法;
分支限界法
- 广度优先搜索(BFS)策略
- 所谓"分支",就是广度优先的策略,依次搜索当前节点的所有分支;
- 抛弃不满足约束条件的相邻节点,其余节点加入"活节点表";
- 然后从表中选择一个节点作为下一个扩展节点,继续搜索
- 限界策略
- 为了加速搜索的进程,一般会在每个活节点处,计算函数值;
- 根据这些已经计算出的函数值,从当前活节点表中选择一个最有利的节点作为扩展节点,使搜索朝着解空间树上最优解的分支推进,以便尽快找出一个最优解;
一些经典算法(要掌握的)
- 二分查找
- 快速排序、归并排序
- KMP算法
- 快慢指针(双指针法)
- 普利姆(Prim)和克鲁斯卡尔(Keuskal)算法
- 迪克斯特拉(Dijkstra)算法
- 其他优化算法:模拟退火、蚁群、遗传算法