目录
一、基础知识
1.基础位运算
<< 左移操作符 &按位与 有0就是0
>>右移操作符 | 按位或 有1就是1
~按位取反 ^异或 相同为1,相异为0 / 无进位相加
例如 1 0 1 1 0 1 ^
1 1 0 1 1 0
结果为 0 1 1 0 1 1,无进位相加就是1 + 0 = 0,1+1 = 0,但是不会向高位进位
2.给一个数n,确定它的二进制表示中的第x位是0还是1
我们规定从右往左,的位数为第0,1,2,3,4,5.....31位
这样右移动的距离等于它的位数
(n >> x) & 1 -> 0 ------ 0
1 ------ 1
3.将一个数n的二进制表示的第x位修改成1
n = n | (1 << x)
4.将一个数n的二进制表示的第n位修改成0
n = n &(~(1 << x))
5.位图的思想
每个比特位0或者1的数值表示一种状态
6.提取一个数n,二进制表示中最右侧的1
lowbit = n & -n(取反加一即变为负)
7.将一个数n二进制表示中最右侧的1抹去
n = n & (n - 1)
8.位运算的优先级
能加括号就加括号
9.异或 ^,运算的运算律
1.a ^ a = 0
2.a ^ 0 = a
3.a ^ b ^ c = a ^ (b ^ c)
二、基础题目示例
1.位1的个数
class Solution {
public:
int hammingWeight(int i) {
int ret = 0;
int n = 32;
while(n > 0)
{
if((i & 1) == 1)ret++;
i = i >> 1;
n--;
}
return ret;
}
};
将要判断的数每次按位与1,若为1则此位为1,然后再右移一位
2.比特位计数
class Solution {
public:
int Add(int i)
{
int ret = 0;
int n = 32;
while(n > 0)
{
if((i & 1) == 1)ret++;
i = i >> 1;
n--;
}
return ret;
}
vector<int> countBits(int n) {
vector<int>ret(n+1,0);
for(int i = 0; i <= n;i++)
{
ret[i] = Add(i);
}
return ret;
}
};
原理同上,只是多了一个循环
3.汉明距离
class Solution {
public:
int hammingDistance(int x, int y) {
int n = 32;
int ret = 0;
while(n>0)
{
if((x & 1) != (y & 1))ret++;
x = x >> 1;
y = y >> 1;
n--;
}
return ret;
}
};
分别用 &1 来判断两个数最低位是0还是1,对两个数进行比较,然后分别不断右移这两个数
4.只出现一次的数字
利用异或的性质即可
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int val = 0;
for(auto ch:nums)
{
val ^= ch;
}
return val;
}
};
5.只出现一次的数字3
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
long long n = 0;
for(auto ch: nums)
{
n ^= ch;
}
long long mask = n &(-n);
mask = (int)mask;
vector<int>ret(2,0);
for(auto ch: nums)
{
if((ch & mask) == mask)ret[0] ^= ch;
else ret[1] ^= ch;
}
return ret;
}
};
我们先将所有值异或,这个过程会将相同的数都消去,最后得到两个不同的数a,b异或的结果。
这个数为1的位置,是两个数上值不同的位置。为了方便,我们取出最右侧的1。
a,b两个数中,一个数的该位置为1,一个数为0.
而待分开的数的该位置也一定为1或0.因此我们将全部数分为两组
a,xx yy zz b,mm,nn,oo
这样即可分别取出单独的数
三、题目演示
1.判断字符是否唯一
class Solution {
public:
bool isUnique(string astr) {
int size = astr.size();
if(size>26)return false;
int bitmap = 0;
for(int i = 0; i < size; i++)
{
int distance = astr[i]-'a';
if(((bitmap >> distance) & 1) == 1)return false;
else
{
bitmap = bitmap | (1 << distance);
}
}
return true;
}
};
这里我们不使用数据结构,我们使用位图来储存信息,达到减小空间复杂度的效果。每个比特位储存一个字符的存在情况,‘a’的位置是右侧第一位,我们记为0下标。
首先我们记录字符串的长度,来进行遍历操作,如果字符串比26长,那么必定有重复
每次进入循环的时候,我们查看这个字符对应位置是否为1,如果是,我们返回fasle,如果不是,我们将位图的此位改为1.
2.丢失的数字
class Solution {
public:
int missingNumber(vector<int>& nums) {
int size = nums.size();
int ret = 0;
for(int i = 0; i < size; i++)
{
ret = ret ^(nums[i]);
}
for(int i = 0; i <= size; i++)
{
ret = ret ^ i;
}
return ret;
}
};
利用异或的性质,例如
我们选取0-5中的5个数字
1 5 2 3 0
0 1 2 3 4 5
我们用0分别遍历异或上下两组数字,上下两组中相同的数字进过异或都变为0,最后只剩下0-5中剩余的那个数字,也即丢失的数字, 0 ^ 这个数字,仍然为这个数字
3.两整数之和
class Solution {
public:
int getSum(int a, int b) {
while(a & b)
{
int tmp = a;
a = a ^ b;
b = (tmp & b) << 1;
}
return a ^ b;
}
};
我们首先要知道,异或运算是一种无进位相加,那我们如何做到有进位呢?只需要把a按位与b即可,这样我们可以知道哪些位置是要进位的,然后再左移一位,相当于进位操作。
但是进位之后相加还可能再次进位,因此我们循环这个过程,我们不断用a^b,a&b更新a和b的值,直到a&b为0。此时无进位。因为任意数异或0等于它本身,因此我们循环结束统一返回a^b即可
4.只出现一次的数字2
class Solution {
public:
int singleNumber(vector<int>& nums) {
int size = nums.size();
int ret = 0;
for(int i = 0; i < 32; i++)
{
int sum = 0;
for(auto ch:nums)
{
sum += (ch >> i)&1;
}
sum = sum % 3;
if(sum)ret = ret | (sum << i);
}
return ret;
}
};
原理如上,可以推广,任意重复n次就对n取余
5.消失的两个数字
class Solution {
public:
vector<int> missingTwo(vector<int>& nums) {
int size = nums.size()+2;
int n = 0;
for(int i = 1; i <= size; i++)
{
n ^= i;
}
for(auto ch:nums)
{
n ^= ch;
}
long long mask = n &(-n);
vector<int>ret(2);
for(int i = 1; i <= size; i++)
{
if((i & mask) == mask)ret[0] ^= i;
else ret[1] ^= i;
}
for(auto ch:nums)
{
if((ch & mask) == mask)ret[0] ^= ch;
else ret[1] ^= ch;
}
return ret;
}
};
与基础题目示例中的5.只出现一次的数字3相似,只是遍历范围是一个数组加上一串连续的数字