目录
1、算法的基本概念
算法是求解问题的一系列计算步骤,用来将输入数据转换为输出结果
1-1.算法的基本特征
有限性 确定性 可行性 输入性 输出性
1-2.算法设计需要满足的目标
正确性 可使用性 可读性 健壮性 高效率和低存储需求
2、时间复杂度计算
2-1.大O表示法:
"大O表示法"表示程序的执行时间或占用空间随数据规模的增长趋势。大O表示法就是将代码的所有步骤转换为关于数据规模n的公式项,然后排除不会对问题的整体复杂度产生较大影响的低阶系数项和常数项
只关注循环执行次数最多的那段代码
加法法则(总复杂度等于量级最大的那段代码的复杂度)
乘法法则(嵌套代码复杂度等于内外代码复杂度的乘积)
2-2.最坏和平均情况
指算法在所有输入I下的所执行基本语句的最多执行次数和平均次数
3、经典算法
3-1.分治法
算法思路
先「分」后「治」,先按照运算符将原问题拆解成多个子问题,然后通过子问题的结果来合成原问题的结果
适用范围
- 该问题的规模缩小到一定的程度就可以容易地解决
- 该问题可以分解成若干个规模较小的相似问题
- 利用该问题分解出的子问题可以合并为该问题的解
- 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子问题
基本步骤
- 分解出若干个子问题
- 求解子问题
- 子问题合并
3-2.贪心法
算法思路
并不从全局最优上考虑,而是每次都做当前的局部最优选择;虽然贪心不是对所有问题都能够得到全局最优解,但事实上很多问题都能够得到
适用范围
使用贪心算法需要满足以下性质:
① 贪心选择性质
该性质是说所求问题的整体最优解可以通过一系列局部最优的选择来达到。而要确定一个问题是否具有这种性质,必须证明每一步所做的贪心选择最终会导致全局最优解
②最优子结构性质
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。这种性质是问题可用动态规划或贪心解决的重要特征
基本步骤
- 建立数学模型描述问题
- 把求解的问题分成若干个子问题
- 把每个子问题求解,得到子问题的局部最优解
- 把子问题的局部最优解合成原来解问题的一个解
3-3.动态规划
算法思路
把多阶段过程转化为一系列单阶段问题,并利用各阶段之间的关系逐个求解
适用范围
①最优化原理(最优子结构性质)
问题的最优解所包含的子问题的解也是最优的
②无后效性
某阶段的状态一旦确定,就不受这个状态以后决策的影响。
③有重叠子问题
子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到
三种常用思路(解题步骤)
下述思路往往是递进的关系
①暴力递归
暴力递归可不简单,要想暴力递归出解法,首先得知道状态转移方程.状态转移方程是用于前后阶段关系,如何列出正确的状态转移方程?
1、确定 base case.
2、确定「状态」,也就是原问题和子问题中会变化的变量.
3、确定「选择」,也就是导致「状态」产生变化的行为.
4、明确 dp 函数/数组的定义.
②带备忘录的递归
由于重叠子问题的存在,暴力递归的效率往往很低,原因在于会重复对某些状态进行递归。因此我们自然而然就想到可以通过备忘录的形式把每个状态的值记录下来,等下次再用到的时候就不用大费周章再去递归一遍,而是直接拿;很显然「备忘录」大大减小了子问题数目,完全消除了子问题的冗余,做到了“聪明的穷举”.
③dp 数组的迭代解法
dp 数组的迭代解法和递归的思路很像,也是需要一个dp数组来记录状态,不过递归解法往往是一个自上而下的过程,而它是自下而上层层迭代的过程——由先前的状态迭代往后得出后面的状态;这种自下而上的思路往往不符合人的惯性思维,解题时往往要搞清楚状态之间的先后关系,必须先遍历初始的状态,再根据状态慢慢演变得出后续的状态,在得到答案之前,它需要遍历所有状态.
算法框架
# 初始化 base case
dp[0][0][...] = base
# 进行状态转移
for 状态1 in 状态1的所有取值:
for 状态2 in 状态2的所有取值:
for ...
dp[状态1][状态2][...] = 求最值(选择1,选择2...)
3-4.回溯法(DFS)
算法思路
解决一个回溯问题,实际上就是一个决策树的遍历过程(其实就是穷举,如果配合着剪枝技巧就是聪明的穷举)
注:DFS使用的数据结构是栈,往往利用递归来解决(递归调用利用的就是系统栈)
算法步骤
针对给定的问题确定解空间
确定结点的拓展搜索规则
以深度优先搜索(DFS)的方式搜索解空间树,并在搜索过程中可以采用剪枝函数来避免无效搜索
算法框架
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
算法技巧
剪枝函数——聪明的穷举:对于一些明显不符合题意的分支进行剪枝,避免无效穷举
数组交换:这个用于解空间为全排列树的情况,而且保存路径的方式为数组,此时可以对数组数据进行交换来保证排列中的每个元素不同