面试经典 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 题 - 数学
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 的平方根 - 二分查找 - 最大小于等于
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;
}
};