动态规划解题套路框架

学习算法不在一招一式,而是培养框架思维。
所谓框架思维,就是套路。不管增删改查,这些代码都是永远无法脱离的结构,你可以把这个结构化作大纲,根据具体问题在框架上添加代码就行了。

一、认识数据结构

数据结构是工具,算法是通过合适的数据结构解决待定为你的方法。

1.1 数据结构存储方式

数据结构的底层存储方式有两种:

  • 数组(顺序存储)
  • 链表(链式存储)

1.2 数据结构种类

  1. 链表
  2. 数组
  3. 散列表
  4. 队列

1.3 数据结构的基本操作

数据结构的操作无非:增删改查

1.4 数据结构遍历 + 访问方式

  • 线性:for/while迭代为代表
  • 非线性:递归为代表

二、如何培养框架思维

  1. 先刷二叉树,因为大部分常考算法本质就是对树的遍历问题。只要涉及递归的问题,基本上都是树的问题。
  2. 从框架看问题,用框架解决问题,不要纠结于细节。
  3. 大量练习。

三、动态规划解题套路框架

3.1 如何判断是否是动态规划问题

动态规划问题的一般形式就是求最值

3.2 动态规划核心问题

动态规划的核心问题就是穷举,要找到最值肯定要把所有值都穷举出来,然后找其中的最值。
当我们遇到求最值的问题时候,一定要先思考如何穷举所有可能结果,这要练成条件反射。

3.3 动态规划三要素

  1. 重叠子问题:动态规划的穷举有点特别,容易对某些项做重复操作,因此我们需要知道两种解决该问题的方法来优化穷举过程“备忘录”和“DP table”,避免不必要的计算。
  2. 最优子结构
  3. 状态转移方程式:其实就是把问题转化为数学方程式,这个是穷举的方案。

3.4 计算时间复杂度

递归的时间复杂度是用子问题个数乘以解决一个子问题需要的时间。

四、备忘录与dp table(优化暴力解法)

备忘录其实就是把我们遍历过的内容保存起来,到下一次遍历,先看看备忘录中有没有该内容,如果有,就把他拿出来直接用。一般用数组或list集合存储。属于自顶向下迭代。

五、动态规划思考框架

动态规划问题的子问题一定是互相独立的

  1. 确定base case(临界状态):也就是迭代终止条件。
  2. 确定“状态”:也就是原问题和子问题中的变量。即不断往base case靠近的变量
  3. 确定“选择”:也就是导致“状态”产生变化的行为

计算机解决问题其实没有任何技巧,他唯一的解决方法就是穷举,因此我们首先思考穷举,进而进行优化,做出“聪明的穷举”

六、典型例题

4.1 斐波那契数列

暴力穷举法

int fib(int n){
	if(n == 0) return 0;
	if(n == 1 || n == 2) return 1;
	return fib(n - 1) + fib(n - 2);
}
				f(20)
			  /		  \
  		f(19)			f(18)
  	   /     \          /   \
  	f(18)	f(17)	  f(17) f(16)
  	...		...			... ...

时间复杂度O(2的N次方)

注意:但凡遇到需要递归解决的问题,最好都画递归树,这是对分析算法复杂度、寻找算法低效的原因都有很大帮助。解决动态规划关键就是写出暴力解法,进而通过备忘录或dp table进行优化。
我们可以看到,在递归过程中,存在重复计算的重叠子问题。比如f(18)和f(17)一共计算了两次。

使用备忘录的递归解法

	int fib(int n){
		if(n == 0) return 0;
		int [] meno = new int [n + 1];
		return helper(meno,n);
	}
	int helper(int[] meno, int n){
		if(n == 1 || n == 2)return 1;
		if(meno[n] != 0) return meno[n];
		meno[n] = meno[n - 1] + meno[n - 2];
		return meno[n];
	|

时间复杂度O(N)
以上两种解法均是自顶向下,也就是从n开始迭代到1or2临界值。而动态规划是自底向上的

dp数组的迭代解法

	int fib(int n){
		if(n == 0) return 0;
		int[] meno = new int[n + 1];
		int meno[1] = 1, meno[2] = 1;
		for(int i = 3;i <= n;i ++){
			meno[i] = meno[i - 1] + meno[i - 2];
		}
		return meno[n];
	}

时间复杂度O(N),空间复杂度O(N)

dp数组的迭代解法(状态压缩)

	int fib(int n){
		if(n == 0) return 0;
		int cur = 1, pre = 1;
		for(int i = 3;i <= n;i ++){
			int sum = cur + pre;
			pre = cur;
			cur = sum;
		}
		return cur;
	}

如果我们发现每次状态转移只需要DPtable中的一部分,那么可以尝试来缩小DP table的大小,只记录必要的数据。
时间复杂度O(N),空间复杂度O(1)

你可能感兴趣的:(算法,面试,算法,java)