位算法
常见位运算总结
位1的个数
给定一个正整数 n
,编写一个函数,获取一个正整数的二进制形式并返回其二进制表达式中
设置位
的个数(也被称为汉明重量)。
示例 1:
输入:n = 11
输出:3
解释:输入的二进制串 1011 中,共有 3 个设置位。
示例 2:
输入:n = 128
输出:1
解释:输入的二进制串 10000000 中,共有 1 个设置位。
示例 3:
输入:n = 2147483645
输出:30
解释:输入的二进制串 1111111111111111111111111111101 中,共有 30 个设置位。
提示:
1 <= n <= 231 - 1
代码如下:
class Solution {
public:
int hammingWeight(uint32_t n) {
int ret=0;
for(int i=0;i<32;i++)
{
if(n&(1<<i))
{
ret++;
}
}
return ret;
}
};
比特位计数
给你一个整数 n
,对于 0 <= i <= n
中的每个 i
,计算其二进制表示中 1
的个数 ,返回一个长度为 n + 1
的数组 ans
作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:
输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
提示:
0 <= n <= 105
代码如下:
class Solution {
public:
int countOnes(int x)
{
int ones=0;
while(x>0)
{
x&=(x-1);
ones++;
}
return ones;
}
vector<int> countBits(int n) {
vector<int>bits(n+1);
for(int i=0;i<=n;i++)
{
bits[i]=countOnes(i);
}
return bits;
}
};
汉明距离
两个整数之间的 汉明距离 指的是这两个数字对应二进制位不同的位置的数目。
给你两个整数 x
和 y
,计算并返回它们之间的汉明距离。
示例 1:
输入:x = 1, y = 4
输出:2
解释:
1 (0 0 0 1)
4 (0 1 0 0)
↑ ↑
上面的箭头指出了对应二进制位不同的位置。
示例 2:
输入:x = 3, y = 1
输出:1
提示:
0 <= x, y <= 231 - 1
代码如下:
class Solution {
public:
int hammingDistance(int x, int y) {
int s=x^y,ret=0;
while(s)
{
ret+=s&1;
s>>=1;
}
return ret;
}
};
只出现一次的数字
给你一个 非空 整数数组 nums
,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
示例 2 :
输入:nums = [4,1,2,1,2]
输出:4
示例 3 :
输入:nums = [1]
输出:1
提示:
1 <= nums.length <= 3 * 104
-3 * 104 <= nums[i] <= 3 * 104
- 除了某个元素只出现一次以外,其余每个元素均出现两次。
代码如下:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int value=0;
for(auto e:nums)
{
value ^=e;
}
return value;
}
};
只出现一次的数字3
给你一个整数数组 nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0]
输出:[-1,0]
示例 3:
输入:nums = [0,1]
输出:[1,0]
提示:
2 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
- 除两个只出现一次的整数外,
nums
中的其他数字都出现两次
代码如下:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
unordered_map<int,int> freq;
for(int num:nums)
{
++freq[num];
}
vector<int> ans;
for(const auto& [num,occ]:freq)
{
if(occ==1)
ans.push_back(num);
}
return ans;
}
};
判断字符是否唯一
实现一个算法,确定一个字符串 s
的所有字符是否全都不同。
示例 1:
输入: s = "leetcode"
输出: false
示例 2:
输入: s = "abc"
输出: true
限制:
0 <= len(s) <= 100
s[i]
仅包含小写字母- 如果你不使用额外的数据结构,会很加分。
解法(位图的思想):
算法思路:
利⽤「位图」的思想,每⼀个「⽐特位」代表⼀个「字符,⼀个 int 类型的变量的 32 位⾜够 表⽰所有的⼩写字⺟。⽐特位⾥⾯如果是 0 ,表⽰这个字符没有出现过。⽐特位⾥⾯的值是 1 , 表⽰该字符出现过。
那么我们就可以⽤⼀个「整数」来充当「哈希表」。
class Solution {
public:
bool isUnique(string astr) {
//利用鸽巢原理做优化
if(astr.size()>26) return false;
int BitMap=0;
for(auto ch:astr)
{
int i=ch-'a';
//先判断是否已经出现过
if(((BitMap>>i)&1)==1) return false;
//把当前字符加入到位图中
BitMap|=1<<i;
}
return true;
}
};
丢失的数字
给定一个包含 [0, n]
中 n
个数的数组 nums
,找出 [0, n]
这个范围内没有出现在数组中的那个数。
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 2:
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。
示例 3:
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。
示例 4:
输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。
提示:
n == nums.length
1 <= n <= 104
0 <= nums[i] <= n
nums
中的所有数字都 独一无二
解法(位运算):
算法思路:
设数组大小为n,那么缺失之前的数据就是[0,n],数组中是在 [0, n] 中缺失⼀个数形成的序列.
如果我们把数组中的所有数,以及 [0, n] ,数组中是在 [0, n] 中缺失⼀个数形成 [0, n] 中的所有数全部「异或」在⼀起,那么根据「异或」 运算的「消消乐」规律,最终的异或结果应该就是缺失的数~
代码如下:
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ret=0;
for(auto x:nums) ret^=x;
for(int i=0;i<=nums.size();i++) ret^=i;
return ret;
}
};
两整数之和
给你两个整数 a
和 b
,不使用 运算符 +
和 -
,计算并返回两整数之和。
示例 1:
输入:a = 1, b = 2
输出:3
示例 2:
输入:a = 2, b = 3
输出:5
提示:
-1000 <= a, b <= 1000
解法(位运算):
算法思路:
◦ 异或 ^ 运算本质是「⽆进位加法」;
◦ 按位与 & 操作能够得到「进位」;
◦ 然后⼀直循环进⾏,直到「进位」变成 0 为⽌
代码如下:
class Solution {
public:
int getSum(int a, int b) {
while(b!=0)
{
int x=a^b;//无进位的结果先算出来
int carry=(a&b)<<1;//算出进位
a=x;
b=carry;
}
return a;
}
};
只出现一次的数字2
给你一个整数数组 nums
,除某个元素仅出现 一次 外,其余每个元素都恰出现 **三次 。**请你找出并返回那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,99]
输出:99
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums
中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
解法(⽐特位计数):
算法思路:
设要找的数位 ret 。
由于整个数组中,需要找的元素只出现了「⼀次」,其余的数都出现的「三次」,因此我们可以根 据所有数的「某⼀个⽐特位」的总和 %3 的结果,快速定位到 0 还是 1 。
这样,我们通过ret 的每⼀个⽐特位上的值,就可以将ret给还原出来
代码如下:
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret=0;
for(int i=0;i<32;i++)// 依次去修改 ret 中的每⼀位
{
int sum=0;
for(int x:nums)// 计算nums中所有的数的第 i 位的和
if(((x>>i)&1)==1)//确定一个数的第X为是0还是1
sum++;
sum%=3;
if(sum==1) ret|=1<<i;//将一个数的二进位数第X位修改成1
}
return ret;
}
};
消失的两个数字
给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字。你能在 O(N) 时间内只用 O(1) 的空间找到它们吗?
以任意顺序返回这两个数字均可。
示例 1:
输入: [1]
输出: [2,3]
示例 2:
输入: [2,3]
输出: [1,4]
提示:
nums.length <= 30000
先将数组中的数和 [1, n + 2] 区间内的所有数「异或」在⼀起,问题就变成了:有两个数出 现了「⼀次」,其余所有的数出现了「两次」
class Solution
{
public:
vector<int> missingTwo(vector<int>& nums)
{
// 1. 将所有的数异或在⼀起
int tmp = 0;
for(auto x : nums) tmp ^= x;
for(int i = 1; i <= nums.size() + 2; i++) tmp ^= i;
// 2. 找出a,b 中⽐特位不同的那⼀位
int diff = 0;
while(1)
{
if(((tmp >> diff) & 1) == 1) break;
else diff++;
}
// 3. 根据diff 位的不同,将所有的数划分为两类来异或
int a = 0, b = 0;
for(int x : nums)
if(((x >> diff) & 1) == 1) b ^= x;
else a ^= x;
for(int i = 1; i <= nums.size() + 2; i++)
if(((i >> diff) & 1) == 1) b ^= i;
else a ^= i;
return {a, b};
}
};