利用二进制的一些特性,我们可以把位运算使用到更多问题上。
例如,我们可以利用二进制和位运算输出一个数组的所有子集。假设我们有一个长度为 n 的 数组,我们可以生成长度为 n 的所有二进制,1 表示选取该数字,0 表示不选取。这样我们就获 得了 2 n 个子集。
(1)342.Power of Four (Easy)
题目描述
给定一个整数,判断它是否是 4 的次方。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
个人思路
首先我们考虑一个数字是不是 2 的(整数)次方:如果一个数字 n 是 2 的整数次方,那么它的二进制一定是 0...010...0 这样的形式;考虑到 n − 1 的二进制是 0...001...1,这两个数求按位与的结果一定是 0。因此如果 n & (n - 1) 为 0,那么这个数是 2 的次方。如果这个数也是 4 的次方,那二进制表示中 1 的位置必须为奇数位。我们可以把 n 和二进制的 10101...101(即0x55555555)做按位与,如果结果不为 0,那么说明这个数是 4 的次方。
代码展示
bool isPowerOfFour(int n) {
if (n <= 0) {
return false;
}
// 检查是否是2的幂
if ((n & (n - 1)) != 0) {
return false;
}
// 检查是否位于奇数位上
return (n & 0x55555555) != 0;
}
(2)318. Maximum Product of Word Lengths (Medium)
题目描述
给定多个字母串,求其中任意两个字母串的长度乘积的最大值,且这两个字母串不能含有相
同字母。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
个人思路
怎样快速判断两个字母串是否含有重复数字呢?可以为每个字母串建立一个长度为 26 的二进制数字,每个位置表示是否存在该字母。如果两个字母串含有重复数字,那它们的二进制表示的按位与不为 0。同时,我们可以建立一个哈希表来存储字母串(在数组的位置)到二进制数字的映射关系,方便查找调用。
代码展示
int maxProduct(vector<string>& words) {
unordered_map<int, int> hash;
int ans = 0;
for (const string& word : words) {
int mask = 0, size = word.size();
for (const char& c : word) {
mask |= 1 << (c - 'a');
}
hash[mask] = max(hash[mask], size);
for (const auto& [h_mask, h_len] : hash) {
if (!(mask & h_mask)) {
ans = max(ans, size * h_len);
}
}
}
return ans;
}
(3)338. Counting Bits (Medium)
题目描述
给定一个非负整数 n,求从 0 到 n 的所有数字的二进制表达中,分别有多少个 1。
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
个人思路
本题可以利用动态规划和位运算进行快速的求解。定义一个数组 dp,其中 dp[i] 表示数字 i的二进制含有 1 的个数。对于第 i 个数字,如果它二进制的最后一位为 1,那么它含有 1 的个数则为 dp[i-1] + 1;如果它二进制的最后一位为 0,那么它含有 1 的个数和其算术右移结果相同,即dp[i>>1]。
代码展示
vector<int> countBits(int n) {
vector<int> dp(n+1, 0);
for (int i = 1; i <= n; ++i)
dp[i] = i & 1? dp[i-1] + 1: dp[i>>1];
return dp;
}