leetcode--只出现一次的数(C语言)

题目:260. 只出现一次的数字 III
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
题目链接:260.只出现一次的数字 III

这道题如果我们不考虑时间复杂度来做题当然是非常的简单,这里我们就不做讨论了;今天我们要在时间复杂度为O(N)(不考虑空间复杂度)的条件下做这道题,其实还是有一定难度的。
在这里插入图片描述

在做题之前首先我们来回顾一下C语言学到的知识 & (按位与)和 ^(按位异或):
&:两个数按位与,两个数的相同二进制位中有0为0,都为1才为1,例如(这里方便观察我们只写了8位)
在这里插入图片描述
^:而两个数按位异或,则是二进制位相同为0,相异为1,例如:
在这里插入图片描述
这里如果我们再将这个得到结果按位异或6,会得到什么?
在这里插入图片描述
结果是5,这不是巧合,开始相当于我们是将0异或了5,0异或任何数会得到任何数,然后再异或了6,最后我们又异或了一个6,那我们异或了两个6,根据异或相同为0,那我们是不是就把原来的6给异或掉了,就只剩下5了。(大家也可以自己试一试其他的用例)

结论异或相当于不进位加法,它可以储存不重复的数字 (1+1=0,1+0=1)。
如果我们从1异或到10,就相当于将这10个数都存起来了,再异或1到10,又会得到0。

知道了这个结论以后,做这道题,大家是不是就有思路了。

**1.**首先我们创建一个变量 ret 初始化为0,然后将它异或原数组中所有的数,这样按照我们刚才的结论,是不是就可以把数组中所有相同的数字给去掉了,ret 中就只剩下两个不相同的数字了。

int* singleNumber(int* nums, int numsSize, int* returnSize){
    int ret =  0;
    int i = 0;
    for(i = 0; i < numsSize; ++i)
    {
        ret ^= nums[i];
    }
}

**2.**既然得到这两个数字,能否得出答案呢?当然不会这么简单,这里我们只是得到了这两个数字相异或的二进制位,接下来我们就思考应该如何将二者分离开?
在这里插入图片描述
我们发现两数异或的结果为1的那一位,两个数必不相同,一个数为1,另一个就必为0,因为异或嘛,相异才能为1,所以我们如果找到异或结果2任意一个为1的那一位,然后根据这一位去分离数组中数,将这一位为0的分在一组,为1的分在另一组,而因为两个相同元素肯定会在同一组,异或时就会异或没了,最后一组只剩下不相同的那一个数,这样就可将两个数分开了。

首先需要找到,异或结果中哪一位为1

int* singleNumber(int* nums, int numsSize, int* returnSize){
    int ret =  0;
    int i = 0;
    for(i = 0; i < numsSize; ++i)
    {
        ret ^= nums[i];
    }
    //寻找异或结果中为1的那一位的位置
     int m = 0;
     while( m < 32)
    {
        if(ret & (1<<m))//将1左移m位和ret相与,为1则条件成立,循环退出
            break;
        else
            ++m;
    }
}

然后就可以将原数组分为两组,这里该怎么储存他们呢?用数组?可我们不知道大小,前面知道了异或,这里我们也可以用异或的方法。

int* singleNumber(int* nums, int numsSize, int* returnSize){
    int ret =  0;
    int i = 0;
    for(i = 0; i < numsSize; ++i)
    {
        ret ^= nums[i];
    }
     int m = 0;
     while( m < 32)
    {
        if(ret & (1<<m))
            break;
        else
            ++m;
    }
    int x1 = 0,x2 = 0;//创建变量x1,x2用来保存两个数
    for(i = 0; i < numsSize; ++i)
    {
        if(nums[i] & (1<<m))
        {
            x1 ^= nums[i];//如果这一位是1则异或到x1上面去
        }
        else
        {
            x2 ^= nums[i];//否则异或到x2上面去
        }
        
    }
}

3. 最后则根据题目要求创建数组,赋值即可

int* singleNumber(int* nums, int numsSize, int* returnSize){
    int ret =  0;
    int i = 0;
    for(i = 0; i < numsSize; ++i)
    {
        ret ^= nums[i];
    }
     int m = 0;
     while( m < 32)
    {
        if(ret & (1<<m))
            break;
        else
            ++m;
    }
    int x1 = 0,x2 = 0;//创建变量x1,x2用来保存两个数
    for(i = 0; i < numsSize; ++i)
    {
        if(nums[i] & (1<<m))
        {
            x1 ^= nums[i];//如果这一位是1则异或到x1上面去
        }
        else
        {
            x2 ^= nums[i];//否则异或到x2上面去
        }
        
    }
    int* retArr = (int*)malloc(sizeof(int)*2);//根据题目要求我们需要创建动态数组,这里不需要释放
    retArr[0] = x1;
    retArr[1] = x2;
    *returnSize = 2;//题目要求我们需要将*returnSize置为2,其实这里很多余。
    return retArr;//最后返回数组
}

总结:这道题的技巧性很强,刚学完C语言第一次做很难把他做出来,特别是后面将两个数分离出来,在学C语言的时候可能都没有注意到 ^ 和 & 这些用法,所以学习编程还是要多做题,多总结,大家一起加油!!

大家学废了吗?有问题的地方欢迎大家在评论区留言指正哦。
互粉互赞~~

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

风继续吹TT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值