1.只出现一次的数字(1)
地址我就不发出来了力扣,牛客都可以资源。
各位还记得按位操作符吗?这个题用按位异或嘎嘎板正嘎嘎牛。
- 按位与(&)有0就是0,全1才是1。
- 按位或(|)有1就是1,全0才是0。
- 按位异或(^)相同为0,相异为1。
看个例子:
也就是说只要是相同的两个数按位异或就是0,如果我把所有的数按位异或,相同的全部消去了,最后按位异或和(简称异或和)就是单身狗的值。
中间那个for循环叫--范围for(名字挺高级,也就这)
- auto:推演出范围变量的数据类型。根据推理,e的类型就是int,也就是auto相当于int
- e:是范围变量的名称,该变量在循环迭代期间接受不同数组元素的值。e相当于nums了。
2.只出现一次的数字(2)
这个比上一个有点意思,有很多意思了。
这个题再用按位异或就会寸步难走。疑惑后重复的消掉了,但是不重复的都保留下来了。
我们能找到两个有力条件
-2^31 <= nums[i] <= 2^31 - 1,这意味着我们处理的数据都是32位的数据。- 其他元素出现三次。
既然是二进制了,那我们让每一位的是都相加,那么某一位如果是3的倍数说明单身狗在这一位为0,奇数位说明单身狗有值。
- 让二进制每一位相加
nums[]={2,2,3,2}
2=00000000000000000000000000000010 2=00000000000000000000000000000010 3=00000000000000000000000000000011 2=00000000000000000000000000000010 sum=00000000000000000000000000000041sum的值不在遵循二进制,它就相当于我们的一个计数器。
- 再将sum进行模3,保留的就是单身狗的。
00000000000000000000000000000041%3=00000000000000000000000000000031
然后把二进制转换为十进制就是结果。
class Solution { public: int singleNumber(vector<int>& nums) { int res = 0; for (int i = 0; i < 32; ++i) { int cnt = 0; for (auto x : nums) { cnt += (x>>i)&1; } res |= (cnt%3)<<i; } return res; } };(num>>i)&1的意思是右移第i位然后按位与1操作是只进行最后一位想加,让其他位为0(因为我们是一位一位来的)。
res |= (cnt%3)<<i;是先取模,然后一位一位左移,再一位一位相加。刚开始res是0,按位或有1就是1。
3.只出现一次的数字(3)
这个也有点意思但是意思不多,甚至不如上面那个有意思,但是这个水平高。
第一个是有一个单身狗,这个有一对单身狗,再按位异或它的异或和还是两个单身狗异或的结果,所以我们得出结论:这个题异或不行在这里我只想说不能光靠异或啊,他又不是美丽国队长。
nums=[1,2,1,3,2,5],异或后的结果是3和5的异或和。现在我咋样才能把他俩分开,就根据唯一的条件异或和把他俩分开。我们想一下第一题,只有一个单身狗,那我们也分两个小组,把这一对单身狗分开,但是我怎样分组,奇数偶数行吗?这个例子就是两个奇数。划界限?怎么划界限?也不行。
那我们想一下
0000 0011 3 ^0000 0101 5 0000 1110按位异或的条件是啥,相同为0,相异为1,也就是说,有1的那一位,两个单身狗的那一位必定不同。第1,2,3位就是。
我们根据位数是1的来进行分组,那这俩单身狗一定在不同组,其他成对的一定分一组。
在不同组我们进行异或即可。
class Solution { public: vector<int> singleNumber(vector<int>& nums) { unsigned int target = 0; // 一对相同的数字异或为0 // 所以target为两个只出现了一次的元素的异或 for (auto n: nums) { target ^= n; } // 因为两数不同,lowbit必然不为0 // 物理意义就是两个不同的数字不同的最低的位在哪 int lowbit = target & (-target); int a1 = 0; int a2 = 0; // 重复上述的过程,但是将nums按照lowbit为1或者为0分类 // 则两个数必然被分到不同的类目;而相同的数字一定在同一个类目 // 所以按类目分别异或就可以得到两个不同的数字 for (auto n: nums) { if (n & lowbit) { a1 ^= n; } else { a2 ^= n; } } vector<int> ans; ans.push_back(a1); //把,a1,a2分别插入ans中 ans.push_back(a2); return ans; } };target=1110,-target=0010(0001+1).
用范围for遍历,我们选定的这一位来分组,这一位是1的在一组,0的在下一组,每一组在按位异或,就能得到两个单身狗了。
4.求最大连续bit数
这个题也需要用到二进制转换这个题有点意思。
其实这个题思路很明确,就是找个计数器count记录连续数字1,max_num记录最大连续数。
这个题中也就是见到1就记录下来。但是如何将十进制转化为二进制的呢?——按位与1就可以了。
#include <iostream> #include <vector> using namespace std; int main(){ int n,max_num=0,count=0; cin>>n; while(n) { if(n&1) { //找每一位是1的。模1还是1 count++; } else { count=0; } n>>=1; } max_num=max(count,max_num); cout<<max_num; }代码就写好了,但是有一点点小岔劈。运行不通过,
改进:
![]()
但是就这一点错误吗?
如果是负数呢?我们输入-1试一试。
![]()
为啥是运行结果过大呢?
-1的二进制是 1111 1111(1111 1110+1) ,当我们右移-1时,左边出现空缺就会补1,一直右移,一直补1,那这辈子也干不完啊。所以我们来个循环。
#include <iostream> #include <vector> using namespace std; int main(){ int n,max_num=0,count=0; cin>>n; for(int i=0;i<32;i++) { if(n&1) { count++; max_num=max(count,max_num); } else { count=0; } n>>=1; } max_num=max(count,max_num); cout<<max_num; }这样就避免了负数的一直补1问题。
本文写的比较仓促,如有问题或错误希望各位大神指正。
这篇博客介绍了如何使用位操作符解决寻找数组中只出现一次的数字的问题。通过位异或操作,可以消除重复的数字,找到只出现一次的数字。对于多个只出现一次的数字,通过分组异或可以分离它们。同时,文章还讨论了寻找最大连续1位数的方法,利用按位与和右移操作实现。










1057





