算法递进笔记之农场牛生牛问题算法及内存解析

来源:互联网 发布:java程序设计课后答案 编辑:程序博客网 时间:2024/05/16 06:21

问题描述:

    农场中有5岁的母牛,开始生小牛,每只牛4年后生且每年都只生一头小头,假定所生的的均为母牛,按照以上牛生牛原则,请问n年后农场有多少牛?

利用罗列的方式看是否有规律:

1年:A  A1

2年:A   A1  A2

3年:A   A1  A2  A3

4年:A   A1  A2  A3  A4

5年:A   A1  A2  A3  A4  A5  A11        /*第五年的牛=上一年的牛+上一年四年前那年的牛可以生小牛(即生出的小牛数)*/

6年:A   A1  A2  A3  A4  A5  A11  A6  A 12  A21

7年:A   A1  A2  A3  A4  A5  A11  A6  A 12  A21  A7  A13  A22  A31 

从以上牛出生的规律发现:一头牛从出生后需要4年才能继续再生牛,当年的牛数是上一年原有的牛以及可以生牛的牛生出的牛数总和

推出一个公式:f(n) = f(n-1)+f(n-4)    n>4

1.结果分析一:

根据以上编写一个方法:

/** * 根据问题中描述牛的出生的规则可以得出一个公式: * f(n) = f(n-1)+f(n-4) */public static int fBronCows(int year){int cowCount = 0;switch (year) {case 1:cowCount = 2;break;case 2:cowCount = 3;break;case 3:cowCount = 4;break;case 4:cowCount = 5;break;}if (year < 5)return cowCount;return fBronCows(year - 1) + fBronCows(year - 4);}


利用递归的方式算得N年总的牛数(条件:在牛不死亡的情况下)。

递归的内存分析:当程序运行时调用方法区的方法模板进行替值计算,第一次进入方法后发现if (year < 5)不满足的情况下继续递归往下算,再次调用fBronCows方法,上次调用情况就直接压入栈底,以次往上计算至中最后不再进入本方法时,从内存栈底自上往下弹出结果最后得出结果如图:

利用数组的另一种算法模式:

public static int fBronCows(int year){int cows[] = new int[year];for (int i = 0; i < year; i++) {if(i==0)cows[0] = 2;if(i==1)cows[1] = 3;if(i==2)cows[2] = 4;if(i==3)cows[3] = 5;if(i>4)cows[i] = cows[i-1] + cows[i-4];}return cows[year-1];}


内在分布情况:以需要求的年数作为数组开辟连接堆内存空间,然后根据以方式一中的算法补充填满数据

根据内存的占用情况对比,明显第二种方式占用内存较低,较高效

2.结果分析二:

 若以面向对象的思维方式来解析此问题将是如下思路:

农场:Farm   母牛:Cow    农场里有牛圈:List<Cow> cows   牛可以生小牛 public void born()   牛每年长大一岁 public void grow()  牛有属于哪个农场的属性,farm 牛有岁数 age等等。

当然我们也可以给牛一些其他属性比如:牛也可以有生日嘛,可以设置一个 birthday  几几年生的嘛,牛也可以用编号,牛也可以有妈妈mother 代码如下:

package cn.com.James;public class Cow {private Farm farm;private int age = 1;private int birthday;private String cowNo;private Cow mother;public Cow(Farm f,int age,int bdy,String no,Cow m){this.farm = f;this.age = age;this.cowNo = no;this.birthday = bdy;this.mother = m;}public void grow(){age++;}/** * 牛生小牛的规律把语言形式描述成代码形式 * @param no  小牛编号 * @param bdy 小牛出生年 * @param m  小牛的妈咪 */public void born(String no,int bdy,Cow m){if(age > 4){Cow c = new Cow(this.farm,1,birthday,cowNo,m);this.farm.addNewCow(c);}}public int getBirthday(){return birthday;}public int getAge(){return age;}}
package cn.com.James;import java.util.ArrayList;import java.util.List;public class Farm {//设置一个牛圈嘛private List<Cow> cows = new ArrayList<Cow>();public Farm(){ cows.add(new Cow(this,5,1990,getCowNo(1990),null));}public void addNewCow(Cow c){cows.add(c);}/** * 通过我看查看每年牛的情况来计算 * @param year出生年 */public void checkYearCows(int year){for (int i = 0; i < cows.size(); i++) {Cow cow = cows.get(i);cow.born(getCowNo(year), year, cow);cow.grow();}}public int getCowsCount(){return cows.size();}public String getCowNo(int year){return year+"-"+System.currentTimeMillis();}}
package cn.com.James;public class FarmCows {public static void main(String[] args) {// TODO Auto-generated method stubFarm f = new Farm();//默认年份int year = 1990;for (int i = 0; i < 20; i++) {f.checkYearCows(year);year++;}System.out.println(f.getCowsCount());}}

通过以上面向对象的思维方式将问题对象化,来处理此问题,相比较之下其通俗性,易懂性完全体现出来了。

附加问题:若在生牛的过程中要把大于8岁数的牛都卖掉呢?

看下面两段代码是否有问题:

List<Cow> cows1 = new ArrayList<Cow>(f.getCows());System.out.println("sale before:"+ cows1.size());for (Iterator iterator = cows1.iterator(); iterator.hasNext();) {Cow cow = (Cow) iterator.next();if (cow.getAge() > 8) {//当以种方式移除时会产生如下异常:原因遍历集合时随意改变内部元素顺序指针紊乱//Exception in thread "main" java.util.ConcurrentModificationException//cows1.remove(cow);iterator.remove();} else {System.out.println(cow.getAge());}}System.out.println("sale after:"+ cows1.size());List<Cow> cows2 = new ArrayList<Cow>(f.getCows());System.out.println("sale before:"+ cows2.size());for (int i = 0; i < cows2.size(); i++) {Cow cow = cows2.get(i);if(cow.getAge()>8){cows2.remove(i);}else{System.out.println(cow.getAge());}}System.out.println("sale after:"+ cows2.size());

1.当第一种方式中以注释代码移除时:cows1不会同步其集合大小至iterator造成iterator指针移动错乱,相反,iterator会直接同步到cows1中删除元素

2.当第二种方式删除时,隐藏的问题是当直接对对象引用进行操作时对象删除后对象下在数组中的下标发生变化全部前移造成部分前移元素不会被遍历到。


0 0