【巧用异或】单身狗2题解

 ✨✨欢迎大家来到Celia的博客✨✨

🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉

所属专栏:【每日刷题】C语言

个人主页Celia's blog~

 题目

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

例如:

有数组的元素是:1,2,3,4,5,1,2,3,4,6

只有5和6只出现1次,要找出5和6.

解题思路 

 这道题可以巧用异或的方式来解答,在此之前,让我们先来了解异或操作符的运算法则:

异或(^):用二进制形式对比两个变量的每一位,如果不同则为1,如果相同则为0。

我们可以得到以下重要推论:

  • n^n=0
  • 0^n=n

 了解了异或操作符的运算法则后,我们可以在此基础上列出此题的大致思路:

先简化一下问题:

如果有一个数组其中只有一个元素单独出现,其他都是成对出现,找出单独出现的元素。

int arr[]={1,2,3,4,5,1,2,3,4};

我们可以遍历这个数组的每一个元素进行异或操作:

int sz = sizeof(arr)/sizeof(int);
int i, ret = 0;//ret初始化为0,这样能保证ret^arr[0]的值为数组第一个元素
for(i = 0; i < sz; i++)
{
    ret ^= arr[i];
}

异或运算符是符合交换律的,遍历一次后相当于上图的操作,最后剩下的就是只出现一次的数字了。

这个简化后的版本和本题的区别是:简化的版本只有一个元素单独出现,本题有两个元素单独出现。我们只需要把本题中的数组分为两组,每一组只有一个元素单独出现,每一组都利用上文的方法,就可以解决此题。那么问题又来了,该按何种方法进行分组呢?

以下是大致思路:

  1. 既然有两个数单独出现,那么单独出现的这两个数一定是不同的两个数。
  2. 既然是不同的两个数,在二进制位上一定有一个或者多个位数是不同的。
  3. 我们可以记录这两个数到底是在第几位二进制数位上是不同的,并且找出其中一个的位置。
  4. 按照这个位置为0或者为1对数组中所有的数进行分组。

有了解题思路,那就先进行第一步:找出这两个数在二进制位上哪个位置是不同的。我们仍然可以遍历数组的每一个元素进行异或操作,这样一来,成对出现的数字互相异或的结果为0,最后剩下的就是两个不同数字异或的结果,这个结果在二进制位上 ‘1’ 的位置就是我们要找的位置。

int arr[]={1,2,3,4,5,1,2,3,4,6};
//5和6只出现一次,其他数字成对出现
int sz = sizeof(arr)/sizeof(int);
int i, ret = 0;
for(i = 0; i < sz; i++)
{
    ret ^= arr[i];//最后是5^6的结果
}

 接下来我们要确定这两个数在二进制的第几位是不同的,确定其中一个位置即可

 for (i = 0; i < 32; i++)//最多有32位
 {
     if (((ret >> i) & 1) == 1)//右移检查每一个二进制位,与1按位与如果等于1,说明这个二进制位为1
     {
        //ret二进制位上的‘1’即为两个数二进制位上的不同之处
         flag++;//二进制位上的数只有0和1两种可能,找到一个不同之处即可,凭此即可区分两个数。
         break;
     }
 }
//以下代码为自定义函数中的代码片段
for (i = 0; i < sz; i++)
{
    if (((arr[i] >> flag) & 1) == 1)//将数组中的每一个元素右移flag位进行判断
    {
        *p1 ^= arr[i];//p1最终的结果将保存第一个单独出现的数,p1初值为0
    }
    else
        *p2 ^= arr[i];//p2最终的结果将保存第二个单独出现的数,p2初值为0
}

这样就变相的将数组中的元素分为两组分别进行遍历异或操作,最终得出结果。

完整代码

#include <stdio.h>
void find_dog2(int arr[10], int* p1, int* p2,int sz)
{
    int i, flag = 0, n = 0;
    for (i = 0; i < sz; i++)
    {
        n ^= arr[i];
    }
    for (i = 0; i < 32; i++)
    {
        if (((n >> i) & 1) == 1)
        {
            flag++;
            break;
        }
    }
    for (i = 0; i < sz; i++)
    {
        if (((arr[i] >> flag) & 1) == 1)
        {
            *p1 ^= arr[i];
        }
        else
            *p2 ^= arr[i];
    }
}
int main()
{
    int arr[10] = { 1,2,3,4,5,1,2,3,4,6 };
    int p1 = 0, p2 = 0;
    int sz = sizeof(arr) / sizeof(int);
    find_dog2(arr, &p1, &p2,sz);
    printf("第一个数字是:%d,第二个数字是:%d", p2, p1);
    return 0;
}

以上就是本题题解的全部内容啦~

如果觉得有所收获的话,记得点赞关注支持一下~

  • 24
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Celia~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值