目录
相信大家都会找简单的“单身狗”,那么三个人一起的话要怎么找单身狗呢 ?
解题思路
我们非从二进制的位去进行分析,对于非单身狗的数,某个二进制为1的话,这个1一定是成三倍出现的,那么某个二进制位的个数膜三余一的话就代表着这个1为单身狗所拥有,我们只需要进行重现记录1的个数然后重现即可
解题方法一
我们先对每一个数的二进制1记录在数组digit中,然后在进行重现
class Solution {
public:
int singleNumber(vector<int>& nums)
{
vector<int>digit(32); //记录位
for (auto e : nums)
{
while (e)
{
int num = e & -e; //找到num中最低位的1
int i = log(num) / log(2); //取对数确定位置
digit[i]++;
e &= e - 1; //消去这个1
}
}
int ans = 0;
for (int i = 0; i < 32; i++)
{
if (digit[i] % 3)
{
ans ^= 1 << i; //数据重现
}
}
return ans;
}
};
证明
代码一 e&-e的作用
首先,我们要知道从e变成-e以后二进制位的变化情况其实是
-e=~e+1;
相信学过位运算的小伙伴们都清楚这一点,那么我们直接来证明一下e&-e可以找到e的二进制位中最低的1吧
首先,如果e中存在1的话,那么e的二进制序列一定是1和0相见或者是全1,我们先简要证明一下全1的情况,全1即-1的二进制按位取反加一可以得到1,-1&1=1,符合题意
接下来证明0和1相见的情况
情况一,二进制最低位为1
情况二,二进制最低位为0
综合以上情况,证明完成
代码二 e&e-1的作用
e&e-1这个代码的作用其实是消去二进制中的最低的1,话不多说,直接开始证明
情况一 二进制最低位为1
情况二 二进制最低位不为1
综上所述,算法一的代码是可行的,可以节省计算二进制位1的操作次数,而不需要对其二进制位进行遍历
该算法时间复杂度总体为O(N),空间复杂度为O(1)
算法二
虽然算法一使用的是常数空间,但还是借用了一个digit数组,我们可以对这个代码进行优化
我们知道一个数的二进制位出现的次数一定为0 1 2,出现两次在出现等价于0
我们需要的是二进制位出现次数为一的数,但是二进制只有0和1两种形式,
所以我们需要使用one和two两个变量来分别进行记录,one中的二进制位1代表这个二进制位的1出现1次,two中的二进制位为1代表这个二进制位出现了两次,最后我们只需要返回one即可
代码如下
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int one = 0, two = 0;
for (auto num : nums)
{
one = one ^ num & ~two;
two = two ^ num & ~one;
}
return one;
}
};
分析代码
因为是对某个二进制位进行分析,所以现在的one和two代指的都是二进制位中的某位
第一步,记录one
1.当two=0时,~two=1
当one等于0时,代表该位之前出现次数为0,当num=1时,代码执行后,one最后等于1,当num=0时,代码执行后one依然是0
当one等于1时,代表该位之前出现的次数为1,当num=1时,代码执行后,one会等于0,当num=0是,代码执行后one依然是1
2.当two=1时,~two=0
此时的one只能为0,并且由于~two为0,&~two后one依然是0
第二步,记录two
1.当one=0时,~one=1
当two等于0时,若num=1,则代表该位之前出现的次数为1(对应第一步中的one等于1,num也等于1的情况),代码执行后two等于1,若num=0,代表的是该位之前出现次数为0,执行代码后,two依然为0
当two等于1时,之前该位出现次数为2,若num=1,代码执行后two为0,若num为1,two不变
2.当one=1时,~one=0
two只能为0,且不管num如何,two始终为0
证明算法
在这个算法的执行过程中,一直有一个原则,就是one和two执行完代码后two不可能同时为1,并且当num等于0的时候,one two不会发生改变,所以我们只需要证明当num为1的时候,且按照代码执行过程分析此前该位出现的次数为0 1 2时代码的执行是否正确
1.该位先前出现的次数为0时
此时one和two都等于0,分析代码执行情况后,one会变成1,two依然是0,这种情况下算法执行步骤正确
2.该位先前出现次数为1时
此时的one等于1,two等于0,分析代码执行情况后,one最终会变成0,而two最终则会变成1,该情况算法执行步骤正确
3.该位先前出现次数为2时
此时的two等于1,one等于0,分析代码执行情况后,one不变,而two会变成0,该情况算法执行步骤正确
综上,该算法证明完成
看到这里,希望小伙伴们能够学会这道题