最近一直在复习一些算法及数据结构方面的东西,就找了一个适合找工作笔试的题目,在剑指Offer上刷了几道题目,发现对复习知识点还是很有用的,做到重建二叉树这块。递归传值出了点问题,debug半小时才找出错误,所有还是写篇博客记录一下。也推荐要找工作的伙伴去剑指Offer刷题。
题目描述
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
一看这个题目可以发现,用2*1的小矩阵去横着或者竖着覆盖大矩形,可以分为两种情况:
1. 竖着放:
可以看出2个2*1的矩阵就是一个组合,很容易发现递归关系式,就是 target = recusion(target - 2),第n项和第n-1项是不可分开的;
2. 横着放:
这一看就跟清楚了,target = recusion(target - 1); 具有最有子结构的特点。
如下代码递归式:
public int RectCover(int target) { if(target < 3 && target > 0){ return target; }else if(target >= 3){ return RectCover(target - 1) + RectCover(target - 2); } return 0; }
可以看到耗时还是挺慢的,其实递归在数据很大的情况下,很容易导致栈溢出。不仅执行时间慢,而且还会计算重复的步骤,下面是一个优化的版本:
记忆化的DP: 避免了递归重复计算的问题,但是增加了空间复杂度
static int[] arrs = new int[1000]; public static int RectCover(int target) { if(target <= 0){ return 0; } if(arrs[target] != 0) { return arrs[target]; } if(target == 1 || target == 2) { arrs[target] = target; }else { arrs[target] = RectCover(target-1)+RectCover(target-2); } return arrs[target]; }
时间快了不少。
这个递归其实跟斐波拉切数列是一样的,我们可以用3个整形变量来存放递归重复算的结果,如下:
public static int RectCover(int target) { if(target < 3 && target > 0){ return target; }else{ int first = 1; int second = 2; int result = 0; for(int i = 3 ; i <= target ; i ++){ result = first + second; first = second; second = result; } return result; } }