剑指offer|解析和答案(C++/Python) (六)

剑指offer|解析和答案(C++/Python) (六)

参考剑指offer(第二版),这里做一个学习汇总,包括解析及代码。代码均在牛客网进行验证(摘自自己的牛客网笔记)。

整个剑指offer解析链接:
剑指offer|解析和答案(C++/Python) (一).
剑指offer|解析和答案(C++/Python) (二).
剑指offer|解析和答案(C++/Python) (三).
剑指offer|解析和答案(C++/Python) (四).
剑指offer|解析和答案(C++/Python) (五)上.
剑指offer|解析和答案(C++/Python) (五)下.
剑指offer|解析和答案(C++/Python) (六).

习题

两个面试案例
1.把字符串转换成数组
(牛客网上部分题是剑指offer的思考题,不多,下面就是那些剩下的题)
2.变态跳台阶
3.矩形覆盖

1.把字符串转换成数组

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
示例1
输入:
+2147483647
1a33
输出:
2147483647
0

思路:
这题本身不难,不够需要注意许多细节。
特殊用例的测试:
1.空指针
2.空字符串""
3.正负号
4.溢出

这里着重讲一下正负数溢出。
参考:
https://zhidao.baidu.com/question/194668811.html
https://zhidao.baidu.com/question/2052829228594311827.html

  • A.负数溢出

这里对应的测数数据为"-2147483649",正确的输出为:0,但很有可能造成错误输出:2147483647。这因为32位的int数据范围是 -2147483648 —— 2147483647
-2147483649 可以看成 -2147483648 + (-1) 。(补充:计算机中数字全部是以补码的形式进行存储)。
-2147483648的原码是:0x80000000,反码是0xFFFFFFFF,补码是0x80000000。
举例理解:-8的补码。
(1)如果用4位二进制数表示的话,原码1000(“1”表示“-”号,“000”可以看成数字位“111”+1的结果)——反码1111——补码1000。
(2)如果用8位二进制数表示的话,原码1000 1000——反码1111 0111——补码1111 1000。
这也是为什么**-2147483648**补码是0x80000000而不是0x180000000的原因,数字被限定用32位int型数据表示。
-1的原码是0x80000001,反码是0xFFFFFFFE,补码是0xFFFFFFFF。
所以 − 2147483649 = − 2147483648 + ( − 1 ) -2147483649 = -2147483648 + (-1) 2147483649=2147483648+(1)0x80000000 + 0xFFFFFFFF =0x7FFFFFFF(补码相加),而0x7FFFFFFF又等于2147483647,这就是负数溢出,造成错误的原因。
如何判断负数溢出:首先判断负数标志位,接着判断最高位是否为0(因为产生了进位)

B.正数溢出
正数溢出和负数溢出类似,正数的最大值为2147483647,输入测试数据2147483648,正确的输出为0,但很容易造成错误输出:-2147483648
一样的分析,2147483648 = 2147483647 + 1
2147483647 原码=反码=补码=0x7FFFFFFF,1的原码=反码=补码=0x00000001。
2147483647 + 1为0x7FFFFFFF + 0x00000001 = 0x80000000,而0x80000000是**-2147483648**的补码,这就是造成错误的原因。
如何判断正数溢出:首先判断正数标志位,接着判断最高位是否为1(因为产生了进位)
代码:
C++

class Solution {
public:
	enum Status{kValid = 0, kInvalid};
	int g_nStatus = kValid;
    int StrToInt(string str) {
        g_nStatus = kInvalid;
		long long num = 0;
		//str.size() 获取str长度 不包括\0
		if(str.size() > 0){
			bool minus = false;//负数的标志
			if(str[0] == '+'){
				str = str.substr(1);//截断字符串
			}else if(str[0] == '-'){
				str = str.substr(1);//截断字符串
				minus = true;
			}
			if(str.size() > 0 && str[0] != '\0'){
				num = StrToIntCore(str, minus);
			}
		}
		return (int)num;
    }
	int StrToIntCore(string str, bool minus){
		int num = 0;
		int length = str.size();
		for(int i = 0; i < length; ++i){
			if(str[i] <= '9' && str[i] >= '0'){//合法的输入
				int flag = minus? -1:1;//正负数
				cout<<"str[i]:"<<str[i]<<endl;
				num = num*10 + flag*(str[i] - '0');
				cout<<"num:"<<num<<endl;
				if((!minus&&((num>>31)&0x1) == 1)||//正数溢出
				(minus&&((num>>31)&0x1) == 0)){//负数溢出
				    cout<<"error"<<endl;
					num = 0;
					break;
				}
			}else{//非法输入
				num = 0;
				break;
			}
		}
		return num;
	}
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, s):
        # write code here
        length = len(s)
        num = 0
        if length > 0:
            minus = False
            if s[0] == '+':
                s = s[1:]#截断
            elif s[0] == '-':
                minus = True#负数
                s = s[1:]
            if len(s) > 0:
                num = self.StrToIntCore(s, minus)
        
        return num
    
    def StrToIntCore(self, s, minus):
        length = len(s)
        num = 0
        for i in range(length):
            if s[i] >= '0' and s[i] <= '9':
                if minus == True:
                    flag = -1
                else:
                    flag = 1
                num = num*10 + flag*(int)(s[i])
                #判断是否溢出
                if (minus == False and ((num>>31)&0x1) == 1) or \
                    (minus == True and ((num>>31)&0x1) == 0):
                    num = 0
                    break
            else:
                num = 0
                break
        
        return num

2.变态跳台阶

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

思路:
1.递归求解。分析递归条件,我们用 f ( i ) f(i) f(i)表示青蛙跳i层台阶存在的跳法。
可以得出条件:
f ( 1 ) = 1 f(1)=1 f(1)=1
f ( 2 ) = 2 f(2)=2 f(2)=2
f ( i ) = f ( i − 1 ) + f ( i − 2 ) + … + f ( 2 ) + f ( 1 ) + 1 f(i)=f(i-1)+f(i-2)+\ldots+f(2)+f(1)+1 f(i)=f(i1)+f(i2)++f(2)+f(1)+1
2.循环:自底向上。根据递归公式得到递推公式:
f ( i ) = f ( i − 1 ) + f ( i − 2 ) + … + f ( 2 ) + f ( 1 ) + 1 f(i)=f(i-1)+f(i-2)+\ldots+f(2)+f(1)+1 f(i)=f(i1)+f(i2)++f(2)+f(1)+1
f ( i − 1 ) = f ( i − 2 ) + … + f ( 2 ) + f ( 1 ) + 1 f(i-1)=f(i-2)+\ldots+f(2)+f(1)+1 f(i1)=f(i2)++f(2)+f(1)+1
f ( i ) − f ( i − 1 ) = f ( i − 1 ) f(i)-f(i-1)=f(i-1) f(i)f(i1)=f(i1)
f ( i ) = 2 × f ( i − 1 ) f(i)=2 \times f(i-1) f(i)=2×f(i1)
f ( i ) = 2 i − 1 f(i)=2^{i-1} f(i)=2i1
于是可以使用循环操作,效率更高。
代码:
C++

class Solution {
public:
    int jumpFloorII(int number) {
		int jumpFloor = 1;
		for(int i = 0; i < number - 1; ++i)
			jumpFloor *= 2;
		return jumpFloor;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def jumpFloorII(self, number):
        # write code here
        jumpFloor = 1
        for i in range(1, number):
            jumpFloor *= 2
        return jumpFloor

3.矩形覆盖

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

思路:
把2n有多少中覆盖方法记为 f ( n ) f(n) f(n)。用第一个21的小矩形去覆盖大矩形的最左边时有两种选择:竖着放或横着放。
当竖着放的时候,右边还剩下2*(n - 1)的区域,这种情形下,覆盖种类有 f ( n − 1 ) f(n-1) f(n1)
当横着放的时候,右边还剩下2*(n - 2)的区域,这种情形下,覆盖种类有 f ( n − 2 ) f(n-2) f(n2)
此时 f ( n ) = f ( n − 1 ) + f ( n − 2 ) f(n)=f(n-1)+f(n-2) f(n)=f(n1)+f(n2)。其实就是斐波拉契数列。
参考图示:
在这里插入图片描述
代码:
C++

class Solution {
public:
    int rectCover(int number) {
		if(number < 3)
			return number;
		int rectOne = 2;
		int rectTwo = 1;
		int rectNums;
		for(int i = 3; i <= number; ++i){
			rectNums = rectOne + rectTwo;
			rectTwo = rectOne;
			rectOne = rectNums;
		}
		return rectNums;
    }
};

Python

# -*- coding:utf-8 -*-
class Solution:
    def rectCover(self, number):
        # write code here
        if number < 3:
            return number
        rectOne = 2
        rectTwo = 1
        for i in range(3, number + 1):
            rectNums = rectOne + rectTwo
            rectTwo = rectOne
            rectOne = rectNums
            
        return rectNums
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值