位运算
- 1.基础位运算
- 2.常见用法总结
- 3.面试题 01.01. 判定字符是否唯一
- 4.丢失的数字
- 5.两整数之和
- 6.只出现一次的数字 II
- 7.面试题 17.19. 消失的两个数字
点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃
1.基础位运算
<<、>>、~、&、| 、^
& : 全为1才为1
| :有1就是1
^ :相同为0,相异为1。 或者无进位相加(不进位)
2.常见用法总结
1.位运算的优先级
能加括号的就加括号
2. 给一个数n,确定它的二进制表示中的第 x 位是0 还是 1
3.将一个数 n 的二进制表示的第 x 位修改成1
4. 将一个数 n 的二进制位表示的第 x 位修改成0
5.位图思想
位图主要借鉴哈希表的思想,哈希表是一个数组或者是一个数组里放链表但不管怎么用都是都一个类型。如果让存42亿个数肯定存不下,这个时候就要用位图了,最小消耗检查一个数据在不在,用比特位,1表示在,0表示不在。
6.提取一个数 n 二进制表示中最右侧的1 (lowbit)
n&(-n)
-n:将最右侧1,左边的区域全部变成相反
7.干掉一个数 n 二进制表示中最右侧的 1
n&(n-1)
n-1:将最右侧的1,右边区域(包括自己)全部变成相反
8.异或(^)运算的运算率
3.面试题 01.01. 判定字符是否唯一
题目链接:面试题 01.01. 判定字符是否唯一
题目描述:
算法原理:
找一个数在不在第一时间应该想到的哈希表,这道题要求不适用额外的数据结构,并且字母都是小写的,因此自己可以写一个int hash[26],将字符一一相对映射。
解法一:哈希表
但是我们还可以更节省空间,记得C++学过的位图,它仅需一个比特位就可以判断一个数到底在不在,每个比特位有两种状态,1表示在,0表示不在
,并且这个范围仅有26个字母,一个int类型完全就够了,因此更节省空间
当然这里还用一个优化:鸽巢原理,假设就n个鸽巢,来了n+1个鸽子,那一定有一个鸽巢是有两只鸽子的。这里也是假设26个字母不重复,但是如果有27个字母那一定有重复!
class Solution {
public:
bool isUnique(string astr) {
// 利用鸽巢原理做优化
if(astr.size()>26) return false;
// 位图
int bitmp=0;
for(auto& ch:astr)
{
int i=ch-'a';
//字符没出现就加入到位图
if((bitmp & (1<<i)) == 0)
bitmp|=(1<<i);
else
return false;//出现就返回
}
return true;
}
};
4.丢失的数字
题目链接:268. 丢失的数字
题目描述:
0~n 应该是n+1个数,但现在只有n个数,肯定是缺了一个。我们要找的就是那个缺的数
算法原理:
解法一:排序+遍历
说到排序还有一种方法,排序之后发现二段性,左边数字和下标对应,右边数字和下标不对应并且右边区域最左端点就是缺少的数字,因此使用二分查找。
解法二:二分查找
上面的时间复杂度比下面高
解法三:哈希
解法四:高斯求和公式
解法五:位运算(异或运算的运算律)
将数组和下面0-n异或,最后一定是缺失的数字。
做法可以是数组里面先异或然后再和0~n异或,或者是数组和下标异或然后和n异或。
class Solution {
public:
int missingNumber(vector<int>& nums) {
int ret=0;
//for(auto e:nums) ret^=e;
//for(int i=0;i<=nums.size();++i) ret^=i;
//return ret;
for(int i=0;i<nums.size();++i)
{
ret^=nums[i]^i;
}
return ret^=nums.size();
}
};
5.两整数之和
题目链接:371. 两整数之和
题目描述:
算法原理:
解法:位运算(异或运算 – 无进位相加)
异或运算就是一个无进位相加的结果
如果我们在能把进制位加上不就是最终结果了吗。
我们的进位是要加到这个无进制位结果的不就是结果了吗,但是这里要求不能使用+号,因此我们要重复上面的工作,因为上面不就是a+b吗
class Solution {
public:
int getSum(int a, int b) {
while(b!=0)
{
int x=a^b;//先算出无进制位相加结果
int array=(a&b)<<1;//算出进位
a=x;
b=array;
}
return a;
}
};
6.只出现一次的数字 II
题目链接:137. 只出现一次的数字 II
题目描述:
算法原理:
如果不限制空间 可以使用计数排序。也可以用一个排序算法然后再遍历找一个只出现一次的数。但是这里限制空间了。时间O(N),空间O(1)。
解法:位运算
计算出单个数字的二进制每一位是多少。
由于其他数字出现3次且对应每个二进制位都是相等的,要不全0要不全1,把这些出现3次的比特位加起来肯定是3的倍数。要不就是3n个0,要不就是 3n个1。而单个数字的对应的二进制位要不是0要不是1。所以把所有元素对应的二进制位加起来再对3取余,就可以确定单个数字,对应每个二进制位是0还是1了。
并且这种做法还可以扩展求其余出现n次,抓出现一次的数。只要%n就可以了
class Solution {
public:
int singleNumber(vector<int>& nums) {
int ret=0;
for(int i=0;i<32;++i)
{
int sum=0;
for(auto x:nums)
if(((x>>i)&1) == 1)
++sum;
sum%=3;
if(sum == 1) ret|=(1<<i);
}
return ret;
}
};
7.面试题 17.19. 消失的两个数字
题目链接:面试题 17.19. 消失的两个数字
题目描述:
一个数组消失了两个数字,原本是1~3 就剩下1了 ,原本1~4就剩下2,3了。
算法原理:
前面做过 丢失数字+只出现一次数字的||| 把它俩结合起来,这道题就非常容易了
首先利用丢失数字的思想来一个1~n的数组(包含丢失两个数的数组),然后将nums和这个数组放在一块,其他数字出现两次,还有两个数字只出现一次。让找出现一次的两个数组,这就转换成只出现一次数字||| 了。
解法:位运算
1.将所有的数异或在一起,tmp
2.找到tmp中,比特位为1 的那一位
因为a和b是不同的数,因此绝对可以找到tmp比特位为1的位置,因为异或只有相异为1,也就说明a和b该比特位是不同的,一个为0,一个为1.
3.根据 找到该比特位 x 的不同,将所有数划分成两类,然后在分别异或
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 中比特位不同的哪一位
//这里找的是最右侧比特位出现1的位置,也可以遍历去找
int bit=tmp&(-tmp);
// 3.根据 该比特位不同,将所有的数划分两类来异或
int a=0,b=0;
for(auto x:nums)
if((x&bit) == 0) a^=x;
else b^=x;
for(int i=1;i<=nums.size()+2;++i)
if((i&bit) == 0) a^=i;
else b^=i;
return {a,b};
}
};