剑指 Offer 56 - I. 数组中数字出现的次数(C实现)

题目

题目链接:剑指 Offer 56 - I. 数组中数字出现的次数
在这里插入图片描述


实现思路:
首先我们知道这道题要找两个不同的数字,除了这两个不同的数字,其他数字都是两两配对的。
我们做过一道题:消失的数字,在这道题中,我们可以很轻松的通过异或找到其中一个不在的数字;我们使用的方式是 相同的数字异或会得到零的方式找到的,而0异或任何数,都不会变;这道题难点在于有两个不相同的数

比如要找一个数组nums[1,1,2,3,3]找到其中的只有一个不同的数字2,我们可以通过 0 ^ 1 ^ 1 ^ 3 ^ 3 ^ 2 得到的结果就是2;那么假如一个数组有两个不同的数字,其他都是两两配对相同的呢?我们就无法直接通过异或得到;

所以我们借助上面的思想:拆分:把有两个不同的数字的数组,分别拆分两个数组,这两个数组,只包含一个不同的数字;
比如:nums[1,1,2,3,4,4,5,5] :拆分成:0 ^ 1 ^ 1 ^ 2 和 0 ^ 4 ^4 ^5 ^5 ^ 3; 那么这样我们就可以找出 2 和 3 啦!(上面的拆分的组合不一定是我的举例的方式,我这里重点关注是拆分,而不是拆分对,要拆分对,请继续看如何拆分的步骤)

现在的要完成是如何拆分:假如我们设两个不同的数为 a ,b ,那么 a ^ b 的二进制位中的某一个位肯定是1(至少有一个二进制位为1),因为异或的性质告诉我们 不同的数字异或为1,知道这个有什么用呢?知道这个我们可以找到这个为1的位置具体在哪里,从而通过它的位置来区分数组中数字的分组
那么是如何通过a^b的二进制中的1位置来确认分组呢?
其实我们只要用这个1的位置来和nums数组中的每个数字按位与一下,这样就可以区分开来了


代码实现

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumbers(int* nums, int numsSize, int* returnSize){
        //开辟存放结果的数组
        int* retNums = (int*)malloc(sizeof(int)*2);

        int x = 0; //保存两个不相同数字的异或结果
        //比如:nums = [4,1,4,6]
        //则 x = 1^6;
        for(int i = 0; i< numsSize;i++){
            x ^= nums[i];
        }

        //找出两个不同数字异或结果x的二进制中为位数1的位置
        int m = 1; //临时变量,得到数为二进制中位数为1的数
        /比如:x = 2^6 = 010^110 = 100
        // x & m = 100 & 001 = 000; 
        // x & m = 100 & 010 = 000;
        // x & m = 100 & 100 = 100; 此时 x &m != 0; 退出循环
        //此时m = 100; 而m二进制中位数为1的位置在最高位
        //(这个位置是由数据决定的,我们只需要找出m从左到右第一个为1的就行)
            while((x & m )== 0) m <<= 1;

            //找到x中二进制为1的位置后,遍历数组与其m 按位与
            //假如x中为1的位置与m与为0,则表示是不同数的其中一个;
            //假如不为0,即为1,那么就是不同数的另外一个        
            int a = 0;
            int b = 0;
            for(int i = 0;i<numsSize;i++){ 
                if((nums[i] & m )== 0)
                    a ^=nums[i];
                else 
                    b ^=nums[i];
            }
                retNums[0] = a;
                retNums[1] = b;
                *returnSize=2;
                return retNums ;
}
  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

呋喃吖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值