1 题目1
如下图所示:找出只出现一次的两个数字,其余都出现了两次。
1.1 题目分析
从整形数组中找出只出现一次的数字,其余都出现了两次。
1.2 分析
-
用 0 异或所有数组中元素,找出出现一次的两个数。
记:temp=0^ 3^ 2^ 3^ 6=2 ^ 6=4(二进制形式:0100) -
找分离temp的分离标志,sep = temp & (-temp )。4&(-4)=4(0100),其中-4在内存中是以补码存储的哦,不要弄错了。
判断语句:4 & 2 (0100 & 0010)= 0(0000) 为零,则跳过,不执行下面语句。
4 & 6(0100&0110)=4(0100)不为零,执行下面语句,找出第一个出现一次的数。
num[0]=0^ 2 (0000^0010)= 2(0010) 此时分离出来了2。 -
分离出来第二个元素。
num[1]=temp^ num[1]=4^ 2 (0100^0010)=6 (0110)。
1.3 C语言参考代码
//0^任何都为任何数,出现两次的数字被异或除掉,只剩出现一次的数字
//分离出来只剩下两个数字异或的内容
for (i = 0; i < numsSize; i++){
temp = temp ^ nums[i];
}
//接下来两个不同的数字的异或中分离出来 不同的数字
sep = temp & (-temp );
//提取第一个数字
for (i = 0; i < numsSize; i++){
if (0 != (nums[i] & sep)){
x = x ^ nums[i];//分离出来第一个数
}
}
//提取第二个数字
y = temp ^ x;//分离出来二个数
num[0] = x;//保存找到的出现一次的数
num[1] = y;
1.4 思路二
具体实现步骤:
- 先让0和全部数组元素异或;找出只出现一次的两个数的异或结果,记为ret;
- 找分离标志位,这里方法是假设分离标志位 为 div = 1 ;
- 继续寻找最合适的分离标志位:
因为ret是异或的结果,ret中二进制位为1的那位,就代表此位对应数组中可分离一个数。此时ret对应的二进制为1 的为可作为分离标志位div ;
图解:
int div = 1;//假设分离标志位
while((div & ret) == 0)
{
div <<= 1;//找出分离标志位
}
- 分离两个出现一次的数
这个代码如下:
int a = 0;
int b = 0;
for(int n:nums)
{
if(div & n) a ^= n;
else b^= n;
}
1.5 C++代码
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int ret = 0;
for(auto n:nums)
{
ret ^= n;
}
int div = 1;//假设分离标志位
while((div & ret) == 0)
{
div <<= 1;//找出分离标志位
}
int a = 0;
int b = 0;
for(int n:nums)
{
if(div & n) a ^= n;
else b^= n;
}
return vector<int>{a,b};
}
};
2 题目2
数字出现的次数值LeetCode137题。
题目描述:
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
2.1 题目分析
目标:
找出只出现一次的数。
难点:
其余数字都出现了3次。
例子:
输入:nums = [2,2,3,2]
输出:3
2.2 解决办法
出现3次的数,有一个特点:其对应二进制位相加都可以被3整除。于是我们可以利用其二进制作为出发点进行操作。
注意:
- 此数组为整数数组,在LeetCode环境下考虑int的32位,循环执行32次。
- 将数组中每个数的对应二进制位相加
total += ((num>>i) & 1);
,记为total。 - 判断相加结果total是否可以被3整除,且没有余数。如果有余数,则将对应的第i位的二进制数置为1如:
ans |= (1<<i);
。ans为返回的只出现一次的元素。
2.3 完整代码
class Solution {
public:
int singleNumber(vector<int>& nums) {
// 位运算解法
int ans = 0;
for(int i=0;i < 32;++i)
{
int total = 0;
for(auto num:nums)
{
total += ((num>>i) & 1);//求对应位1的个数
}
// 如果对应位取余不等于三,则说明不整除,此位对应数字出现不是三次
if(total % 3)
{
ans |= (1<<i);//将对应位置为1
}
}
return ans;
}
};
解决此问题还有一个很牛的代码,有兴趣的话可以看看,具体分析在这里。
class Solution {
public:
int singleNumber(vector<int>& nums)
{
//数字电路解法
int a = 0, b = 0;
for (auto x : nums)
{
b = (b ^ x) & ~a;
a = (a ^ x) & ~b;
}
return b;
}
};