剑指 Offer(专项突击版)第1|2题

2,181 阅读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 - 1b != 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(log⁡N),即为二分查找需要的时间。
  • 空间复杂度:O(log⁡N)。

剑指 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

  • 结尾依旧:长风破浪会有时,直挂云帆济沧海!
  • 在线整理的确实很好,对掘金的文章进行了一个汇总整理,在线刷题指南,拿走不谢,要学会站在别人的肩膀上提升自己点击这里-->  前端进阶指南

image.png

传送门