面试题64、65、66、67

面试题64.求1+2+…+n

在这里插入图片描述
本题使用暴力法和迭代法都必须借助乘除或者条件判断语句,所以这两种方法都不可取。那如果使用递归,有没有除了 if 等判断语句之外的其他方法来终止递归?

在这里插入图片描述

  • Java 中,为构成语句,需加一个辅助布尔量 x ,否则会报错;
  • Java 中,开启递归函数需改写为 sumNums(n - 1) > 0 ,此整体作为一个布尔量输出,否则会报错;
class Solution {
    int res = 0;
    public int sumNums(int n) {
		boolean x = n > 1 && sumNums(n - 1) > 0;
		res += n;
		return res;
    }
}
  • 时间复杂度 O(n) : 计算 n+(n−1)+…+2+1 需要开启 n 个递归函数。
  • 空间复杂度 O(n) : 递归深度达到 n ,系统使用 O(n) 大小的额外空间。

———————————————————————————————————————

面试题65.不用加减乘除做加法

在这里插入图片描述

如果是十进制,我们是如何完成加法计算的?

  • 99 + 111 = ?

1.个、十、百位的数字分别相加先不管进位的问题

个位:9 + 1 = 0
十位:9 + 1 = 0
百位:0 + 1 = 1
得到临时结果:100

2.计算进位的数字

1 + 9 = 10
10 + 90 = 100
得到进位结果:10 + 100 = 110

3.相加得结果

100 + 110 = 210

如何用二进制来实现加法?

  • 15 + 12 = ?

12 二进制:1100
15 二进制:1111

1.各位置上的数字分别相加先不管进位的问题:

1100 + 1111 = 0011
得到临时二进制结果:0011

2.计算进位的数字

0100 + 0100 = 1000
1000 + 1000 = 10000
得到进位结果:11000

3.相加得到结果

0011 + 11000 = 11011(十进制:27)

第一步骤不用加法如何得到相同结果?

异或:相同为0,不同为1
1100 ^ 1111 = 0011

第二步骤不用加法如何得到此结果?

如果同一个位置上的数字相与能得到1,则说明这两个数在这一位置上都是1,则相加后就会产生进位,相与后往左移一位,即得到进位
(1100 & 1111) << 1 = 11000

第三步骤不用加法如何得到结果?

第三步是前两步的和,还是个加法;如果不用加法,就只能不断调用前两步的步骤。

在这里插入图片描述
从上图可以看出,这个就是在不断重复第一,第二的步骤,那么退出条件是什么?
如果进位为0,则这个循环就会一直进行下去,永远不会停,所以终止条件就是进位=0

class Solution {
    public int add(int a, int b) {
		while(b != 0) {
			int temp = (a & b) << 1; //计算a+b的进位
			a = (a ^ b); //计算非进位和
			b = temp;
		}
		return a;
    }
}
  • 时间复杂度 O(1): 最差情况下(例如 a= 0x7fffffff , b=1 时),需循环 32 次,使用 O(1) 时间;每轮中的常数次位操作使用 O(1) 时间。
  • 空间复杂度 O(1): 使用常数大小的额外空间。

———————————————————————————————————————

面试题66.构建乘积数组

在这里插入图片描述
用两个数组,一个数组维护数的左侧的乘积和,一个数组维护数的右侧的乘积和,最后再将这两个数组上的数分别相乘,得到最后的结果
在这里插入图片描述

class Solution {
    public int[] constructArr(int[] a) {
        if(a == null || a.length < 1) return new int[0];
        int len = a.length;
        int[] left = new int[len];
        int[] right = new int[len];
        left[0] = 1;
        right[len - 1] = 1;
        //求左侧乘积和
        for(int i = 1; i < len; i++) {
            left[i] = left[i - 1] * a[i - 1];
        }
        //求右侧乘积和
        for(int i = len - 2; i >= 0; i--) {
            right[i] = right[i + 1] * a[i + 1];
        }
        int[] res = new int[len];
        for(int i = 0; i < len; i++) {
            res[i] = left[i] * right[i];
        }
        return res;
    }
}

优化

class Solution {
    public int[] constructArr(int[] a) {
        if(a.length == 0) return new int[0];
        int[] b = new int[a.length];
        b[0] = 1;
        int tmp = 1;
        for(int i = 1; i < a.length; i++) {
            b[i] = b[i - 1] * a[i - 1];
        }
        for(int i = a.length - 2; i >= 0; i--) {
            tmp *= a[i + 1];
            b[i] *= tmp;
        }
        return b;
    }
}
  • 时间复杂度 O(N) : 其中 N 为数组长度,两轮遍历数组 a ,使用 O(N) 时间。
  • 空间复杂度 O(1) : 变量 tmp 使用常数大小额外空间(数组 b 作为返回值,不计入复杂度考虑)。

———————————————————————————————————————

面试题67.把字符串转换成整数

在这里插入图片描述

有以下四种字符需要考虑:

  • 首部空格:删除之即可
  • 符号位:三种情况,即 ‘’+’’ , ‘‘−’’ , ''无符号" ;新建一个变量保存符号位,返回前判断正负即可。
  • 非数字字符:遇到首个非数字的字符时,应立即返回。
  • 数字字符:转换并拼接
    在这里插入图片描述

数字越界处理:

在每轮数字拼接前,判断 res 在此轮拼接后是否超过 2147483647,若超过则加上符号位直接返回。
设数字拼接边界 bndry = 2147483647 / 10 = 214748364 ,则以下两种情况越界:
在这里插入图片描述

class Solution {
    public int strToInt(String str) {
        char[] c = str.trim().toCharArray();
        if(c.length == 0) return 0;
        int res = 0, bndry = Integer.MAX_VALUE / 10;
        int i = 1;
        int sign = 1; //符号位
        if(c[0] == '-') sign = -1;
        else if(c[0] != '+') i = 0;
        for(int j = i; j < c.length; j++) {
            if(c[j] < '0' || c[j] > '9') break;
            if(res > bndry || res == bndry && c[j] > '7') 
            	return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            res = res * 10 + (c[j] - '0');
        }
        return sign * res;
    }
}
  • 时间复杂度 O(N) : 其中 N 为字符串长度,线性遍历字符串占用 O(N) 时间。
    空间复杂度 O(N) : 删除首尾空格后需建立新字符串,最差情况下占用 O(N) 额外空间。

若不使用 trim() / strip() 方法,而从头开始遍历字符串,则可以将空间复杂度降低至 O(1)

class Solution {
    public int strToInt(String str) {
        int res = 0, bndry = Integer.MAX_VALUE / 10;
        int i = 0, sign = 1, length = str.length();
        if(length == 0) return 0;
        while(str.charAt(i) == ' ')
            if(++i == length) return 0; //超出长度
        if(str.charAt(i) == '-') sign = -1;
        //如果有符号位,则往后跳过这个符号位
        if(str.charAt(i) == '-' || str.charAt(i) == '+') i++;
        for(int j = i; j < length; j++) {
            if(str.charAt(j) < '0' || str.charAt(j) > '9') break;
            if(res > bndry || res == bndry && str.charAt(j) > '7')
                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            res = res * 10 + (str.charAt(j) - '0');
        }
        return sign * res;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值