剑指 Offer(专项突击版)第1|2题
前言
- 现在前端要求变高了,找工作的时可能会碰上算法题,每天刷几道算法题做足准备,今天是《剑指 Offer(专项突击版)》第1|2题。
剑指 Offer II 001. 整数除法
给定两个整数 a 和 b ,求它们的除法的商 a/b ,要求不得使用乘号 '*'、除号 '/' 以及求余符号 '%' 。
难度:简单
注意:
- 整数除法的结果应当截去(truncate)其小数部分,例如:truncate(8.345) = 8 以及 truncate(-2.7335) = -2
- 假设我们的环境只能存储 32 位有符号整数,其数值范围是[−2^31, 2^31−1] 。本题中,如果除法结果溢出,则返回 2^31-1
示例 1:
输入:a = 15, b = 2 输出:7 解释:15/2 = truncate(7.5) = 7
示例 2:
输入:a = 7, b = -3 输出:-2 解释:7/-3 = truncate(-2.33333..) = -2
示例 3:
输入:a = 0, b = 1 输出:0
示例 4:
输入:a = 1, b = 1 输出:1
提示:
● -2^31 <= a, b <= 2^31 - 1
● b != 0
方法一:二分查找
分析:
题目限制不能使用乘号和除号。一个直观的解法是基于减法实现除法。这种解法的时间复杂度为O(n)。需要对这种解法进行优化。
将上述解法稍做调整。当被除数大于除数时,继续比较判断被除数是否大于除数的2倍,如果是,则继续判断被除数是否大于除数的4倍、8倍等。如果被除数最多大于除数的2k倍,那么将被除数减去除数的2k倍,然后将剩余的被除数重复前面的步骤。由于每次将除数翻倍,因此优化后的时间复杂度是O(logn)。
/**
* @param {number} dividend
* @param {number} divisor
* @return {number}
*/
var divide = function(dividend, divisor) {
const MAX_VALUE = Math.pow(2, 31) - 1;
const MIN_VALUE = -Math.pow(2, 31);
// 考虑被除数为最小值的情况
if (dividend === MIN_VALUE) {
if (divisor === 1) {
return MIN_VALUE;
}
if (divisor === -1) {
return MAX_VALUE;
}
}
// 考虑除数为最小值的情况
if (divisor === MIN_VALUE) {
return dividend === MIN_VALUE ? 1 : 0;
}
// 考虑被除数为 0 的情况
if (dividend === 0) {
return 0;
}
// 一般情况,使用类二分查找
// 将所有的正数取相反数,这样就只需要考虑一种情况
let res = 0;
let flag = '';
if (dividend < 0 && divisor > 0 || dividend > 0 && divisor < 0) {
flag = '-';
}
dividend = Math.abs(dividend);
divisor = Math.abs(divisor);
while (dividend >= divisor) {
let temp = divisor, m = 1;
while (temp <= (dividend >> 1)) { // 位运算模拟乘法,撑到最大。防止溢出
temp <<= 1;
m <<= 1;
}
dividend -= temp;
res += m;
}
return parseInt(flag + res);
};
复杂度分析
- 时间复杂度:O(logN),即为二分查找需要的时间。
- 空间复杂度:O(logN)。
剑指 Offer II 002. 二进制加法
给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。
输入为 非空 ****字符串且只包含数字 1 和 0。
难度:简单
示例 1:
输入: a = "11", b = "10" 输出: "101"
示例 2:
输入: a = "1010", b = "1011" 输出: "10101"
提示:
● 每个字符串仅由字符 '0' 或 '1' 组成。
● 1 <= a.length, b.length <= 10^4
● 字符串如果不是 "0" ,就都不含前导零。
方法一:模拟
分析:
可以参照十进制加法来完成二进制加法。在进行十进制加法时,总是将两个数字的右端对齐,然后从它们的个位开始从右向左相加同一位置的两个数位,如果前一位有进位还要加上进位。
二进制加法也可以采用类似的方法,从字符串的右端出发向左做加法。与十进制不同的是,二进制是逢二进一,当两个数位加起来等于2时就会产生进位。
/**
* @param {string} a
* @param {string} b
* @return {string}
*/
var addBinary = function(a, b) {
let i = a.length - 1;
let j = b.length - 1;
let r = 0;
let res = '';
while (i >= 0 || j >= 0 || r > 0) {
if(i >= 0 ) r += 1* a[i--];
if(j >= 0 ) r += 1* b[j--];
res += r % 2;
r >>= 1;
}
return res.split('').reverse().join('');
};
复杂度分析
假设 n=max{∣a∣,∣b∣}
- 时间复杂度:O(n),这里的时间复杂度来源于顺序遍历 a 和 b。
- 空间复杂度:O(1),除去答案所占用的空间,这里使用了常数个临时变量。
so
传送门
转载自:https://juejin.cn/post/7170311942635487240