递归与循环(斐波那契数列,位运算)

若是咱们须要重复的屡次计算相同的问题,能够选择递归循环两种方法。算法

递归:是在函数的内部调用这个函数自身,而循环则是经过设置算法的初始值和终止条件,在一个范围内重复运算。一般基于递归实现的代码比基于循环实现的代码要简洁不少,更加易于实现。但它同时也有显著的缺陷:递归因为是函数调用自身,而函数调用是有时间和空间的消耗的(每一次函数调用,都须要在内存栈中分配空间以保存参数,返回地址以及临时变量,而往栈里压入数据和弹出数据都须要时间。);另外,递归调用中可能有不少计算都是重复的,从而对性能带来很大的负面影响。递归还有可能引发严重的问题:调用栈溢出。函数

斐波那契数列:写一个函数,输入 n,求斐波那契数列的第 n 项。性能

1. 效率低的解法(递归):spa

不难发现树中有不少重复节点,并且重复的节点随着n 的增大而急剧增长,事实上,用递归方法计算的时间复杂度是以 n 的指数的方式递增的。递归

//递归求解:
int Fibonacci_1(int n)
{
 if(n == 0)
  return 0;
 if(n == 1)
  return 1;
 return Fibonacci_1(n - 1) + Fibonacci_1(n - 2);
}
内存

2. 实用的解法(循环):时间复杂度为O(N)。ci


//循环求解
int Fibonacci_2(int n)
{
 int result[2] = {0, 1};
 if(n < 2)
  return result[n];
 int fibNMinusOne = 1;
 int fibNMinusTwo = 0;
 int fibN = 0;
 for(int i = 2; i <= n; i++)
 {
  fibN = fibNMinusOne + fibNMinusTwo;
  fibNMinusTwo = fibNMinusOne;
  fibNMinusOne = fibN;  
 }
 return fibN;
}效率

斐波那契数列的不一样应用:变量

        1)一只青蛙一次能够跳上1 级台阶,也能够跳上 2 级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法?循环

分析: 先考虑最简单的情形,假设只有一级台阶时,显然只有一种跳法。若是有两级台阶时,那就有两种跳法:一种是分两次跳,每一次只跳一级台阶;另一种就是一次跳 2 级。

接着讨论通常状况,把 n 级台阶时的跳法当作是 n 的函数 F(n),当 n > 2 时,第一次跳的时候就有两种不一样的选择:一是第一次只跳一级台阶,此时跳法的数目等于后面剩下的 n - 1级台阶的跳法数目,记为F(n - 1)。另一种选择是第一次跳 2 级,此时,跳法数目等于后面剩下的 n - 2 级台阶的跳法数目,记为 F(n - 2)。所以,n 级台阶的不一样跳法的总数为 F(n) = F(n - 1) + F(n - 2)。

        2)如图:可使用 2 * 1 (左)的小矩形横着或者是竖着去覆盖更大的矩形,请问用 8 * 2 * 1 的小矩形无重叠的覆盖一个 2 * 8 的大矩形(右),总共有多少种方法?

分析:先把 2 * 8 的覆盖方法记为 F(8) ,用第一个 1 * 2小矩形去覆盖大矩形的最左边时有两种选择,竖着放或横着放。当竖着放的时候,右边还剩下 2 * 7的区域,这种状况下的覆盖方法记为 F(7)。接下来考虑横着放的状况。当 1 * 2 的小矩形横着放的时候,而在右边还剩下 2 * 6 的区域,这种情形下的覆盖方法记为F(6)。所以, F(8) = F(7) + F(6)。【为斐波那契数列】

(二) 位运算

问题:二进制中 1 的个数。输入一个二进制数,输出其中 1 的个数

分析:

一个基本思路:先判断二进制表示中最右边移位是否是 1, 接着把输入的整数右移一位,此时原来处于从右边数起的第二位被移到了最右边,再判断是否为1。这样每次移动一位,知道整个整数都变成 0 为止。可是,此时当输入的二进制数为一个负数时,容易进入死循环(由于,负数右移以后,左边补符号位 1)。为了不死循环,能够不右移输入的数字 n,首先将 n 与 1 作与运算,判断 n 的最后一位是否为1. 接着把 1 左移一位,获得 2,再和 n 作与运算,判断输入数据的第二位是否为 1. 这样反复左移,每次都能判断 n 的其中一位是否为 1 。

int NumberOf1(int n)
{
 int count = 0;
 unsigned int flag = 1;
 while(flag)
 {
  if(n & flag)
   count++;
  flag = flag << 1;
 }
 return count;
}

经典算法:咱们发现把一个整数减去 1,都是把左右边的 1 变成 0,若是它的右边还有 0 的话,全部的 0 都变成 1,而它的左边全部位都保持不变。接下来将一个二进制数和它减去 1 的结果作位于运算,会把该二进制数最右边的 1 变成 0。那么一个二进制数表示中,有多少个 1,就能够进行多少次这样的操做。


int NumberOf_1(int n)
{
 int count = 0;
 while(n)
 {
  ++count;
  n = n & (n - 1);
 }
 return count;
}

相关题目:1)输入两个二进制数 m 和 n ,计算须要改变 m 的多少位才能获得 n。

                                解决方法:第一步求这两个数的异或,第二步统计异或结果中 1 的个数。

                2)用一条语句判断一个整数是否是 2 的整数次方。

                                解决方法:一个整数若是是 2 的整数次方,那么它的二进制表示中有且只有一位是 1,而其余全部位均为 0,根据以前的分析,将这个数减去 1 以后,再和它本身作与运算,这个整数中惟一的 1 就会变成 0。

相关文章
相关标签/搜索