算法面试题,轻言“算法”太匆匆——也谈“兔子”面试题

园子里面试题目盛行,今天看到一个关于“兔子”数量的问题,回复很多,但感觉都不得要领,答案也是千奇百怪、五花八门。这里给出我的一个写法。
题目: 一对小兔子一年后长成大兔子;一对大兔子每半年生一对小兔子。大兔子的繁殖期为4年,兔子的寿命是6年。假定第一年年初投放了一对小兔子,试编程计算,第n年末总共会有多少对兔子。n由键盘输入。(用递归哦,谢谢!)(http://www.cnblogs.com/zuozuo/archive/2011/09/18/2180426.html)
1.理解问题 每次看到这种题目,我总是胆战心惊,担心自己把题目理解错。自然语言中的“一年后”、“每半年”、“繁殖期为4年”、“寿命是6年”以及“第n年末”其实一不小心就会弄错,而一旦弄错,结果必然谬之千里。 编程的首要原则是什么?我认为是理解问题(Understand the problem first!参见:http://programmers.stackexchange.com/questions/91527/what-do-you-consider-the-1st-principles-of-programming)。在考虑算法之前,必须理解要解决的问题是什么。如果连问题都不清楚,怎么可能谈得上算法呢? 很多人一旦拿到题目,就立刻认为自己已经理解了问题。然而对于我来说,我宁愿花几倍的时间来理解问题。下面就是我对题目的理解过程: 首先我假设一个具体的起始时间(人笨,只好用笨办法):2000年1月1日。从这天起有了最初的那1对小兔子(并且假设它们为0岁)。由于“大兔子每半年生一对小兔子”且“兔子的寿命是6年”,6年是半年的整数倍数,所以兔子的数量每半年变化一次。因此每半年考察一次兔子的数目。
0岁 半岁(+) 1岁 1.5岁 2岁 2.5岁 3岁 3.5岁 4岁 4.5岁 5岁 5.5岁 (总) 00年01月01日 1 1 00年07月01日 1 1 第1年末 01年01月01日 1 1 2 01年07月01日 1 1 1 3 第2年末 02年01月01日 2 1 1 1 5 02年07月01日 3 2 1 1 1 8 第3年末 03年01月01日 5 3 2 1 1 1 11 03年07月01日 8 5 3 2 1 1 1 21 第4年末 04年01月01日 13 8 5 3 2 1 1 1 34 04年07月01日 21 13 8 5 3 2 1 1 1 55 第5年末 05年01月01日 33 21 13 8 5 3 2 1 1 1 88 05年07月01日 54 33 21 13 8 5 3 2 1 1 1 142 第6年末 06年01月01日 86 54 33 21 13 8 5 3 2 1 1 227 06年07月01日 139 86 54 33 21 13 8 5 3 2 1 1 366 第7年末 (注:用EXCELL做这个表极其方便)
由于“兔子的寿命是6年”,所以不可能有6周岁的兔子 由于“繁殖期为4年”,所以只有1~4.5岁的兔子有生育能力 特别要注意的是,“第n年末”的含义则意味着经历了2*n-1次繁殖。所谓第7年年末就是06年7月1日到06年12月31日之后的数量。这个期间兔子数量没有变化。
2.代码 根据前面的分析,这个题目的算法很简单,几乎没有研讨的必要。重要的是建立适当的数据结构。 用一个数组来表示各年龄兔子的数目即可: unsigned rabbits [12]; 其中,rabbits[0]表示0岁兔子数目,rabbits[1]表示半岁兔子数目……,由于不可能有6周岁的兔子,所以数组只需要12个数据。 每过半年,兔子长半岁。意味着数组各个元素依次向后移动。最后一个数据,由于不存在超过6周岁的兔子,所以舍弃。每次移动后根据rabbits[2]~rabbits[9](繁殖期为4年)的总数计算新出生的兔子数目(rabbits[0])。 计算第n年末兔子的数目,用一个简单的2*n-1次循环就可以完成,这实在谈不上什么算法。不过既然题目要求递归,那么好吧
/* 题目: 一对小兔子一年后长成大兔子;一对大兔子每半年生一对小兔子。 大兔子的繁殖期为4年,兔子的寿命是6年。 假定第一年年初投放了一对小兔子,试编程计算,第n年末总共会有多少对兔子。 n由键盘输入。(用递归哦,谢谢!) */ #include #include #define COUNT_IN_HALF(years) ( 2 * ( years ) ) #define GROW_UP ( COUNT_IN_HALF(1) )//一对小兔子一年后长成大兔子 #define REPRODUCTION_BEGIN ( GROW_UP ) #define REPRODUCTION_END ( GROW_UP + COUNT_IN_HALF(4) )//大兔子的繁殖期为4年 #define LIFE_END ( COUNT_IN_HALF(6) ) //兔子的寿命是6年 #define SIZE(a) ( sizeof(a)/sizeof(*a) ) unsigned breed ( unsigned [], size_t , unsigned ); unsigned sum ( unsigned [], size_t ); int main( void ) { unsigned rabbits [LIFE_END] //不存在满6周岁的兔子 = { 1U } ; unsigned n ; printf("即算第几年末的兔子数?\n"); scanf("%u",&n); printf( "%u\n" , breed ( rabbits , SIZE( rabbits ) , COUNT_IN_HALF( n ) ) ); system("PAUSE"); return 0; } unsigned breed ( unsigned rabbits[] , size_t size , unsigned half_years ) { size_t i ; // half_years - 1 次 if( half_years == 1 ) return sum( rabbits , size ); //兔子长半岁 for( i = size - 1 ; i > 0 ; i --) rabbits[i] = rabbits [ i - 1 ]; //繁殖 for( rabbits[0] = 0 , i = GROW_UP ; i < REPRODUCTION_END ; i ++ ) rabbits[0] += rabbits [ i ]; return breed( rabbits , size , half_years - 1 ); } //索性也递归 unsigned sum ( unsigned rabbits[] , size_t size ) { if( size == 0) return 0 ; return sum( rabbits , size - 1 ) + rabbits[size - 1]; }
感想:1.先看懂题。2.良好的数据结构往往不需要什么复杂的算法。
Tags:  华为面试题 java面试题 面试题 百度算法面试题 算法面试题

延伸阅读

最新评论

发表评论