每天两题01
又是两个月的时间过去了,上一次写博客是7月14号,时间还是过的很快的,那么问题来了,为什么这么长时间都没有写东西了呢?难道是在打酱油?
哈哈,说起来很惭愧,刚刚开始工作,碰到各种的问题要去学习要去解决,然后业余的时间又去学了一些奇奇怪怪的东西,导致博客一直都落下了,归根到底,还是自己懒惰了,因为心中总觉得今天又工作了一天,下班了要好好放松一下。不自觉的用这种心理在安慰自己,使得自己越来越放松了,然后又因为自己有点拖延症,想要写点东西就一直拖着。。。
╮( ̄▽ ̄")╭哎,话不多说了,最近源码什么的看的多了总觉得自己基础还是不够扎实,就想着业余打打基础,上班时间再看看框架写写业务逻辑什么的应该比较好,每天打基础的东西不多,就两个题,贪多嚼不烂,题目选自剑指offer,有兴趣的可以在github中自己看看哦,链接:https://github.com/CyC2018/CS-Notes
第一题:斐波那契数列
题目描述:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。 n<=39
我的思路:什么是斐波那契数列应该还是知道一点的,公式如上图所示;简单的来说就是从第三个数开始,任意一个数等于前面两个数之和,比如0,1,1,2,3,5,8,13,22.......那么最简单的思路就是用递归,看下面代码:
public class Study01 { public static int feibo(int n){ //要有下面这两个if,记住递归的话要有出口,然后会有死循环 if (n==0) { return 0; } if (n==1 || n==2) { return 1; } return feibo(n-1)+feibo(n-2); } public static void main(String[] args) { int res = feibo(6); System.out.println(res);//8 } }
上面的代码写出来了,但是可不可以优化一下,因为递归会导致一些值重复的计算,例如计算feibo(5)=feibo(4)+feibo(3)=[ feibo(3)+ feibo(2)] + feibo(3),然后这里计算机会计算两次feibo(3),这里计算机可不会像人一样合并同类项然后计算啊,而一旦计算feibo(n),当n的值很大的时候,在递归计算过程中就会有很多的这种重复计算的数,那么我们有没有办法将这种重复计算的数给剔除呢?只计算一次然后存起来,下一次再计算的话直接去拿就好了;
改进1:新建一个数组,我们从n=0开始,将每次算出来的数放到数组中,那么数组中第n个位置的数就是我们需要的结果
//改进方式1 public static int feiboUp01(int n){ if (n==0) { return 0; } if (n==1 || n==2) { return 1; } int[] arr = new int[n+1]; arr[0]=0; arr[1]=1; arr[2]=1; //这个循环每一次都会就算出来一个值填充到数组中,直到算出arr[n] for (int i = 3; i <= n; i++) { arr[i]=arr[i-1]+arr[i-2]; } return arr[n]; } public static void main(String[] args) { int res = feiboUp01(6); System.out.println(res);//8 }
改进二:不知道有没有发现上面这种做法虽然巧妙的利用了数组,但是对于我们来说,除了数组中的最后一个数,其他的数都是没有什么必要的,难道计算一个非常大的n,就要new一个这么大的数组吗?所以我们还可以继续改进一下;
//改进方式2 public static int feiboUp02(int n){ if (n==0) { return 0; } if (n==1 || n==2) { return 1; } //这里的三个变量,比如0,1,1,2,3,5,当current为5的时候,pre就是3,prepre就是2 int prepre=1; int pre=1; int current = 0; //这里有点不好理解,三个数prepre pre current //经过一次循环,计算current = prepre+pre,这个时候也要将prepre和pre往右移动一个位置,将 //原来的pre指向现在的current,原来的prepre指向现在的pre for (int i = 3; i <= n; i++) { current = prepre+pre; prepre = pre; pre = current; } return current; } public static void main(String[] args) { int res = feiboUp02(6); System.out.println(res); }
第二题:我们可以用 2x1 的小矩形横着或者竖着去覆盖更大的矩形。请问用 n 个 2x1 的小矩形无重叠地覆盖一个 2xn 的大矩形,总共有多少种方法?
这个问题其实还是斐波那契数列,但是思路很有趣,一般我们想办法计算有多少种方法的时候,可能就去画图计算去了,但是这里却是将一个大的问题拆成子问题,而子问题可以继续拆...
如果n=1,只有一种情况,
如果n=2,只有两种,两块都横着或者两块都竖着
假如n=3,那么问题就变成用 3个 2x1 的小矩形无重叠地覆盖一个 2x3 的大矩形,第一种情况,填充的那一块是横着放的,如下所示,那么剩下的需要填充的就是相当于n=2的情况,即2x2的情况;第二种情况就是第一块填充的是竖着放的,那么还需要再填充一块,那么剩下的就是n=1的情况;
依次类推,当n=5的时候也有两种情况,第一种情况横着填充一块,剩下的就是n=4的那种;第二种情况竖着填充两块,剩下的就是相当于n=3的那种,通用公式如下,就不多说了,代码的话和上面基本一样,就不多说了。。。
这两个题目还是很有意思的,可以说一个题目是让你看公式写代码,而另外一个题目其实用的是分治法,所谓的分治法,就是分而治之。就是将原问题划分为n个规模较小,结构与原问题类似的小问题进行处理,递归地解决这些问题,然后再合并求解的过程。