剑指Offer66题之7-12

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/muyiyushan/article/details/88360201

第7题:斐波那契数列

题目

大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。n<=39

解析

斐波那契数列的定义:

  • F 0 = 0 , F 1 = 1 F_0 = 0, F_1 = 1 ;
  • F n = F n 1 + F n 2 ( n 2 ) F_n = F_{n-1} + F_{n-2} ( n \geq 2)
    可以从 F 0 = 0 , F 1 = 1 F_0 = 0, F_1 = 1 开始;,不断向前计算,最后得到 F n F_n ; 也可以采用递归
class Solution {
public:
    int Fibonacci(int n) {
        if (n == 0)
            return 0;
        if (n == 1)
            return 1;
        int f1 = 0, f2 = 1, ret;
        // 1)前向计算
        for (int i = 2; i <= n; i++)
            ret = f1 + f2, f1 = f2, f2 = ret;
        // 2) 递归方法
        /* 
        ret = Fibonacci(n-1) + Fibonacci(n-2);
        */        
        return ret;
    }
};

第8题:跳台阶

题目:

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解析

最简单的动态规划,考虑最后跳上第n阶台阶,有两种方式往上跳:

  • 从第n - 1阶往上跳一级到第n阶;
  • 从第n - 2阶往上跳二级到第n阶。
    那么 f ( i ) f(i) 表示该青蛙跳上一个i级的台阶总共跳法 f ( i ) = f ( i 1 ) + f ( i 2 ) , f ( 0 ) = f ( 1 ) = 1 f(i)=f(i−1)+f(i−2),f(0)=f(1)=1 这就是变形版的斐波拉契数列,直接求解。
class Solution {
public:
    int jumpFloor(int n) {
        if (n == 1)
            return 1;
        int f1 = 1, f2 = 1, ret;
        for (int i = 2; i <= n; i++)
            ret = f1 + f2, f1 = f2, f2 = ret;
        return ret;
    }
};

第9题: 变态跳台阶

题目:

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

解析

根据上题可以写出下面的公式:
f ( n ) = f ( 0 ) + f ( 1 ) + + f ( n 1 ) , f ( 0 ) = 1 f(n)=f(0)+f(1)+⋯+f(n−1),f(0)=1
注意到: f ( 0 ) + f ( 1 ) + + f ( n 2 ) = f ( n 1 ) f(0)+f(1)+⋯+f(n−2)=f(n−1)
那么: f ( n ) = f ( 0 ) + f ( 1 ) + + f ( n 2 ) + f ( n 1 ) = 2 f ( n 1 f(n)=f(0)+f(1)+⋯+f(n−2)+f(n-1)=2f(n-1 )
这是一个首项为1,公比为2的等比数列,直接可以得到最终表达式 f ( n ) = 2 n 1 f(n)=2^{n−1}

class Solution {
public:
    int jumpFloorII(int number) {
    	// 将1左移number-1位
        return 1 << (number - 1);
    }
};

备注

C++中, i<<num为将i的二进制左移num位,对应十进制放大 2 n u m 2^{num} 次方,同理>>为右移运算符

第10题:矩形覆盖

题目:

我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

解析

类似跳台阶的动态规划问题,考虑覆盖最后 f ( n ) f(n) 可能的情况,可能是 f ( n 1 ) f(n-1) 之后竖着覆盖一个,有可能是 f ( n 2 ) f(n-2) 后横着覆盖两个,所以 f ( n ) = f ( n 1 ) + f ( n 2 ) f(n)=f(n-1)+f(n-2) ,另外, f ( 1 ) = 1 f(1)=1 (竖着覆盖一个), f ( 2 ) = 2 f(2)=2 (竖着2个,或横着2个).

class Solution {
public:
    int rectCover(int number) {
        if (number == 0)
            return 0;
        if (number == 1)
            return 1;
        if (number == 2)
            return 2;
        int f1 = 1, f2 = 2, ret;
        for (int i = 3; i <= number; i++)
            ret = f1 + f2, f1 = f2, f2 = ret;
        return ret;
    }
};

第11题:二进制数中1的个数

题目

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

解析

  • 思路1:先判断整数二进制最右边一位是不是1.接着把输入的整数右移一位,此时原来处于右边数第二位的移到最右边了,再判断是不是1(i.e. n&1).这样每次移动一位,直到整个整数变成0为止。如果n是正数,右移一位最左边补0,若是负数,右移之后最左边补1,会陷入死循环。思路1否决。
class Solution {
public:
     int  NumberOf1(int n) {
         int ret = 0;
         while (n>0)
         {
         	if ((n&1) == 1)
         		ret++;
         	n = n >> 1;
         } 
         return ret;
     }
};
  • 思路2:不右移n。首先n与1做与运算,判断n的最低位是不是1。然后把1左移一位,再与n做与运算,就能判断i的低次位是不是1。不断左移,就能不断判断n的每一位是不是1. 思路2正确。
class Solution {
public:
     int  NumberOf1(int n) {
         int ret = 0;
         uint flag = 1;
         while (flag>=1)
         {
         	if ((n&flag) > 0)
         		ret++;
         	flag = flag <<1;
         } 
         return ret;
     }
};
  • 思路3.把一个整数减去1,再和原来的整数做与运算,会把该整数最右边一个1变成0。那么一个整数的二进制表示中有多少个1,就可进行多少次这样操作。如果没有1了,此操作结果即为0.
class Solution {
public:
     int  NumberOf1(int n) {
         int ret = 0;
         while (n>0)
         {
			ret++;
			n = (n-1)&n;
         } 
         return ret;
     }
};

备注

此题考查位运算
位逻辑运算符 : & (与),^ (异或), | (或),~ (取反). 移位运算符:<< (左移),>> (右移)。
&运算通常用于二进制取位操作,例如一个数 &1的结果就是取二进制的最末位。
^运算通常用于对二进制的特定一位进行取反操作,^ 运算的逆运算是它本身,也就是说两次异或同一个数最后结果不变,即(a ^ b) ^ b=a;
a<<b表示把a转为二进制后左移b位(在后面添加 b个0),a<<b的值实际上就是a乘以2的b次方,通常认为a<<1比a*2更快,因为前者是更底层一些的操作
a>>b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们经常用>>1来代替 /2

第12题:数值的整数次方

题目

给定一个double类型的浮点数baseint类型的整数exponent。求base的exponent次方。

解析

二分求解 b a s e e x p = ( b a s e e x p / 2 ) 2 ( b a s e   o r   1 ) base^{exp} = (base^{exp/2})^2*(base\ or\ 1)
计算时判断exp的正负奇偶
时间复杂度: O ( l o g n ) O(logn)

class Solution {
public:
    double Power(double base, int exponent) {
    	//判断exponent的正负
        bool f = exponent < 0;
        // 如果exponent为0,返回1
        if ((exponent = abs(exponent)) == 0)
            return 1.0;
        // 计算二分解
        double ret = Power(base, exponent / 2);
        // 根据奇偶判断
        ret = exponent % 2 ? ret * ret * base : ret * ret;
        //根据正负判断
        return f ? 1.0 / ret : ret;
    }
};

参考:
剑指Offer66题之每日6题 - 第二天
剑指Offer面试题:9.二进制中1的个数

猜你喜欢

转载自blog.csdn.net/muyiyushan/article/details/88360201
今日推荐