前言
由于要准备找工作了,所以要开始刷题了,但是平时疏于做题,一些知识已经悄然忘记,特开此专栏记录我复习的过程
今天做牛客上一道位运算的题,刚开始是暴力求解,然后就想到了位运算。
知识讲解
首先,位运算是指将数字转换为二进制的情况下进行的操作
5= 0 1 0 1
6= 0 1 1 0
位运算分为逻辑位运算符和位移位运算符
逻辑位运算符
位与(&)
当两者都为1时,结果为1
16 & 5
|
V
0 1 1 1 0
0 0 1 0 1
---------
0 0 1 0 0
位或(|)
当且仅当两位都为0时,结果为0
13 | 5
|
v
0 1 1 1
0 1 0 1
-------
0 1 1 1
异或(^)
当且仅当两位不同时,结果为1
13 ^ 5
|
v
0 1 1 1
0 1 0 1
-------
0 0 1 0
所以异或就有一个性质,就是任何数异或自己都等于0
所以就有 a^b^a=b
用异或还可以进行数据交换
a=a^b
b=a^b
a=a^b
这里就完成了数据的交换
按位取反( ~ )
将所有的数字0变成1,1变成0
13
0 1 1 1
|
V
~13
1 0 0 0
位移运算符
左移(<<)
x<<y就表示将x转换为二进制,将所有的位都向左移动y位,然后末位补0,每移动一位就相当于这个数乘了2倍
5<<2
0 0 0 1 0 1
|
v
0 1 0 1 0 0
20
右移(>>)
x>>y则表示将x转换为二进制,将所有的位都向右移动y位,每移动一位就相当于给x除了2,接着向下取整
然后,还分为了两种情况,如果x为正则高位补0,x为负则高位补1(这里涉及到了二进制的正负,如果是有符号整数,最高位是符号位,为0则是正数,为1则是负数)
5>>2
0 0 0 1 0 1
|
v
0 0 0 0 0 1
1
题目思路
这里题目是找出一个数组中只出现一次的两个数,刚好就能用到异或的性质,由于一个数异或自己等于0,而0异或任何数都等于那个数
所以首先就定义一个变量a,赋值为0,然后遍历数组,将这个变量与数组中的每个值都异或一遍,由于大部分数都会出现两次,异或后都是0,最后a的值就是那两个只出现一次的值异或的结果
然后,因为两个数不相等,所以总会有某一位,一个数是1,而另一个数是0
5 = 0 1 0 1
4 = 0 1 0 0
这里的5和4就是,最低位首先就不相同
所以,第二步就是定义一个变量b,赋值为1,然后与a进行异或,再通过左移运算符一步步找到两个出现次数为1的数的第一个不相同的位
再然后遍历原数组,通过与b进行与运算,将原数组分成了两部分,然后定义两个变量,分别与这两部分的值进行异或,就能求出出现次数为1的那两个数
题目还要求按升序返回,简单用if语句判断一下,然后用swap交换一下就行
#include <fstream>
#include <vector>
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型vector
*/
vector<int> FindNumsAppearOnce(vector<int>& nums) {
int a=0;
//首先用a与数组中的每个数都异或一遍
for(int num:nums)
{
a^=num;
}
int b=1;
//接着找出两个出现一次的数第一个不同的位
while((b&a)==0)
{
b<<=1;
}
//接着定义两个变量分别求出那两个数
int num1=0,num2=0;
for(int num:nums)
{
if(num&b)
{
num1^=num;
}
else
{
num2^=num;
}
}
if(num1>num2) swap(num1, num2);
vector<int> v={num1,num2};
return v;
}
};