刷题 位运算 / 数学

面试经典 150 题 - 位运算

⭐️⭐️67. 二进制求和

在这里插入图片描述

加法进位

class Solution {
public:
    string addBinary(string a, string b) {
        int na = a.size(), nb = b.size();
        string ans;
        ans.reserve(max(na, nb) + 1); // 预留空间,避免动态扩展时的性能损耗
        int carry = 0;
        for (int i = na - 1, j = nb - 1; i >= 0 || j >= 0 || carry > 0; --i, --j) {
            int x = (i >= 0) ? a[i] - '0' : 0;  // 从 a 中取值
            int y = (j >= 0) ? b[j] - '0' : 0;  // 从 b 中取值
            int sum = x + y + carry;
            carry = sum / 2;  // 更新进位
            ans.push_back('0' + (sum % 2));  // 将当前位加入结果
        }
        reverse(ans.begin(), ans.end());  // 反转结果,保证顺序正确
        return ans;
    }
};

⭐️⭐️⭐️190. 颠倒二进制位

在这里插入图片描述

class Solution {
public:
    // 思考:如何取 n 的最后一位? (n & 1)
    // 思考:如何取 n 的倒数第二位?(n >> 1) & 1
    // 如何 取出 倒数第二位 并赋值给 另一个数字的第二位?
    // ans = ans | (((n >> 1) & 1) << 31)
    uint32_t reverseBits(uint32_t n) {
        uint32_t ans = 0;
        for (int i = 0; i < 32; ++i) {
            ans = ans | ((n & 1) << (31 - i));
            n >>= 1; 
        }
        return ans;
    }
};

⭐️191. 位1的个数

class Solution {
public:
    int hammingWeight(int n) {
        int ans = 0;
        for (int i = 0; i < 32 && n > 0; ++i) { // n = 0 可以提前截止
            ans += (n & 1);     // 取最后一位
            n >>= 1;            // 将n右移一位
        }
        return ans;
    }
};

⭐️136. 只出现一次的数字 - 异或运算

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        for (auto& num : nums) {
            ans ^= num;
        }
        return ans;
    }
};

⭐️⭐️⭐️⭐️137. 只出现一次的数字 II - 一个数字出现一次 - 其他出现三次

在这里插入图片描述

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int sum = 0;
            for (auto& num : nums) {
                sum += ((num >> i) & 1); // 求第i位上的和
            }
            ans |= (sum % 3) << i;
        }
        return ans;
    }
};

⭐️⭐️⭐️⭐️201. 数字范围按位与 - 最长公共前缀

数学 + 位运算:找到左右边界公共前缀位
在这里插入图片描述

    int rangeBitwiseAnd(int left, int right) {
        int ans = 0;
        for (int i = 0; i < 32; ++i) {
            int a = (left >> (31 - i)) & 1;
            int b = (right >> (31 - i)) & 1;
            if (a != b) {
                break;
            } else {
                ans |= (a << (31 - i));
            }
        }
        return ans;
    }

代码优化

    // 直接一直右移直到相等
    int rangeBitwiseAnd(int left, int right) {  
        int shift = 0;
        while (left != right) {
            left >>= 1;
            right >>= 1;
            ++shift;
        }
        return left << shift;
    }

面试经典 150 题 - 数学

9. 回文数

转换成字符串

class Solution {
public:
    bool isPalindrome(int x) {
        string s = to_string(x);
        for (size_t i = 0, j = s.size() - 1; i < j; ++i, --j) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
};

⭐️⭐️ ⭐️⭐️反转一半 - 对0和1000的特判 - 奇数和偶数

在这里插入图片描述

class Solution {
public:
    // 123321
    // 12332    1
    // 1233     12
    // 123      123 --> a <= b,结束

    // 12321
    // 1232     1
    // 123      12
    // 12       123 --> a <= b,结束

    // 对于 1000 这种不符合以上规律
    // 100
    // 10       0
    // 1        0
    // 0        1   --> 这样反而符合要求
    // --> 将尾部有 0 的直接剔除

    bool isPalindrome(int x) {
        if (x == 0) return true;
        if (x < 0 || x % 10 == 0) return false;
        int half_x = 0;
        while (x > half_x) {
            half_x = half_x * 10 + x % 10;
            x /= 10;
        }
        return (x == half_x) || (x == half_x / 10);
    }
};

66. 加一

在这里插入图片描述

不优雅写法

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int carry = 0;
        for (int i = digits.size() - 1; i >= 0; --i) {
            int sum = carry + digits[i] + (i == digits.size() - 1);
            digits[i] = sum % 10;
            carry = sum / 10;
            if (carry == 0) break;
        }
        if (carry > 0) {
            vector<int> result(digits.size() + 1);
            result[0] = carry;
            for (int i = 1; i <= digits.size(); ++i) {
                result[i] = digits[i - 1];
            }
            return result;
        } else {
            return digits;
        }
    }
};

优雅写法

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        int carry = 1;  // 初始进位为1,因为要加1
        for (int i = digits.size() - 1; i >= 0; --i) {
            int currentSum = digits[i] + carry;
            digits[i] = currentSum % 10;  // 更新当前位
            carry = currentSum / 10;      // 计算进位
            if (carry == 0) return digits; // 如果没有进位,直接返回
        }
        // 如果有剩余进位
        digits.insert(digits.begin(), carry);
        return digits;
    }
};

⭐️172. 阶乘后的零

class Solution {
public:
    // 2 * 5 会产生尾随0 --> 取决于 5 的数量
    // 5 10 15 20 25(两个5) 30 35 40 45 50 55 60 65 70 75(两个5) 85 95 ...
    // 有几个 5 的倍数      --> +1
    // 有几个 5^2 的倍数    --> +2 (但由于在5的倍数中被加过一次) --> +1
    // 有几个 5^3 的倍数    --> +3 (但由于在5的倍数和 5^2 的倍数中被加过一次) --> +1
    // ...
    int trailingZeroes(int n) {
        int sum = 0;
        while (n >= 1) {
            n = n / 5;
            sum += n;
        }
        return sum;
    }
};

⭐️⭐️69. x 的平方根 - 二分查找 - 最大小于等于

在这里插入图片描述

50. Pow(x, n) - 快速幂 - 注意题目n可能为负数

class Solution {
public:
    // eg. x ^ 10
    // 10 --> 1010 --> 8 + 2
    // 10 = 2^3 + 2^1
    // x^10 = (x^2)^3 * (x^2)
    double myPow(double x, int n) {
        double y = x, result = 1;
        bool is_negitive = (n < 0);
        n = abs(n);
        while (n != 0) {
            cout << (n & 1) << endl;
            if (n & 1) {
                result *= y;
            }
            y *= y;
            n >>= 1;
        }
        if (is_negitive) return 1 / result;
        return result;
    }
};
class Solution {
public:
    // 本题二分查找第一个小于等于 target 的值
    int mySqrt(int x) {
        if (x == 0 || x == 1) return x;
        int left = 1, right = x / 2;  // 上界直接设置为 x / 2 即可
        while (left <= right) {
            // 循环不变量
            // (left - 1) < x / (left - 1)
            // (right + 1) > x / (right + 1)
            int mid = left + (right - left) / 2;
            if (mid == x / mid) {
                return mid;
            } else if (mid < x / mid) {
                left = mid + 1;
            } else {
                right = mid - 1;
            }
        }
        // 最终是 left = mid = right, 注意有 (right + 1) > x / (right + 1)
        // 如果 mid < x / mid --> left = mid + 1 = right + 1, 可以返回 right
        // 如果 mid > x / mid --> right = mid - 1 = left - 1, 可以返回 right
        return right;
    }
};

❤️149. 直线上最多的点数

哈希表

霍夫变换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值