【剑指offer】一个单身狗和两个单身狗的问题@面试题56

本文通过一个有趣的单身狗故事介绍了如何利用异或操作在O(n)时间复杂度内找到数组中缺失的或者只出现一次的数字。通过异或性质,将所有数字异或和数组异或,找出不同位,再按该位划分数组进行异或,从而找到目标数字。文章提供了详细代码示例,解释了两种情况:寻找一个唯一数字和寻找两个唯一数字的方法。
摘要由CSDN通过智能技术生成


正文开始

1. 前情提要 —— 一个单身狗的故事

数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

示例1 ——
输入:[3,0,1]
输出:2
示例2 ——
输入:[9,6,4,2,3,5,7,0,1]
输出:8

题目链接:消失的数字

这个“消失的数字”问题本质可以转化为单身狗问题来解决。

异或的运算性质 —— 同0异1

  • 0异或任何数结果还是这个数
    0 ⨁ a = a 0\bigoplus a =a 0a=a
  • 任何数异或本身都是0
    a ⨁ a = 0 a \bigoplus a = 0 aa=0

🔑思路

那么,我们就异或遍历0到n所有整数,再异或遍历nums这个数组中每一个元素。
由异或运算符性质可知,相同的数字都被异或掉了,剩下的那个就是只出现一次的数字

此问题就已经转化为 —— 单身狗问题

一个数组中只有一个数字是出现一次,其他所有数字都出现了两次。编写一个函数找出这个只出现一次的数字。

🔑代码如下 ——

int missingNumber(int* nums, int numsSize){
   int x = 0;
   int i = 0;
   for(i = 0; i<=numsSize; i++)
   {
       x ^= i;
   }
   for(i = 0; i<numsSize; i++)
   {
       x ^= nums[i];
   }
   return x;
}

2. 两个单身狗的故事

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例1 ——
输入:nums = [4,1,4,6]
输出:[1,6][6,1]
示例2 ——
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10][10,2]

题目链接:两个单身狗的故事

由上一题的经验,我们很容易的想到异或的方法

🔑思路

💛分组 ——

  • 把这两个的只出现一次的数字各自分到两个组中
  • 其余数字成对分到两个组中去
  • 分别遍历异或两组,就可以拿到这两个单身狗数字了

那么如何分组呢?

输入:nums = [1,2,3,4,5,1,2,3,4,6]
输出:[5,6][6,5]

既然这两个数字不同,那么它们的二进制表示中,一定有一位不同。
用这个例子来说,我们可以以最低位来划分,也可以以第二位来划分 ——

[101] - 5
[110] - 6
分组:
[110 6 2 2 4 4 ]   
[101 5 1 1 3 3 ][110 6 2 2 3 3 ]
[101 5 1 1 4 4 ] 

❄️ 先把所有数字都异或在一起,计算出的ret,即是5^6的结果

5 - 101
6 - 110 异或(01)011

ret的二进制表示是1的位即是,5和6的不同位,这样就找到了分组的依据。

❄️之前的学习中,我们知道,二进制位的每一位我们都是可以拿到的,只需要配合>>&1即可。我们就可以找到这两个单身狗数字的一个不同的二进制位的位置pos

再用这个位置来划分这两个组即可。

完整代码 :

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* singleNumbers(int* nums, int numsSize, int* returnSize){
    int* arr = (int*)malloc(sizeof(int)*2);
    *returnSize = 2;
    //1.把所有数字都异或在一起
    int i = 0;
    int ret = 0;
    for(i = 0; i < numsSize; i++)
    {
        ret ^= nums[i];
    }

    //2.找到这两个单身狗数字的,不同的二进制位の位置
    int pos = 0;
    for(i = 0; i < 32; i++)
    {
        if(((ret>>i) & 1) == 1)
        {
            pos = i;
            break;
        }
    }

    //3. 分组,边分组边异或
    int m = 0;
    int n = 0;
    for(i = 0; i < numsSize; i++)
    {
        if(((nums[i]>>pos)& 1) == 1)
        {
            m ^= nums[i];
        }
        else
        {
            n ^= nums[i];
        }
    }

    arr[0] = m;
    arr[1] = n;

    return arr;
}

3. 尾声

💜这两个单身狗就是这样幸福的生活在一起~
就是天意让他们在茫茫人海中找到彼此吧!哈哈

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

浮光 掠影

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

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

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

打赏作者

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

抵扣说明:

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

余额充值