Leetcode刷题日记三(面试题56 - II. 数组中数字出现的次数 II)经典寻找“单身狗”题目

目录

解题思路

解题方法一

证明

代码一  e&-e的作用

代码二  e&e-1的作用

算法二

分析代码

证明算法


 

 

相信大家都会找简单的“单身狗”,那么三个人一起的话要怎么找单身狗呢 ?

解题思路

我们非从二进制的位去进行分析,对于非单身狗的数,某个二进制为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,该情况算法执行步骤正确

综上,该算法证明完成

看到这里,希望小伙伴们能够学会这道题

 

  • 29
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 43
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 43
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

build小春宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值