力扣 剑指 Offer 56 - I. 数组中数字出现的次数

题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof

题目要求

一个整型数组 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]

限制:
2 <= nums.length <= 10000


题解

由于题目对时间复杂度和空间复杂度有要求,那么使用两重循环嵌套暴力寻找(时间复杂度为O(n^2))是不可行的。因此,考虑使用异或操作来实现。

由于相等的两个数按位异或之后的值为0,那么将数组中所有的数字异或操作起来,所得的数就是我们要寻找的两个落单数字的按位异或结果。
例如:

4 ^ 1 ^ 4 ^ 6 => 1 ^ 6
6 对应的二进制: 110
1 对应的二进制: 001
1 ^ 6 二进制: 111

然而,我们只知道异或的结果,无法反推出这两个数。这时,就需要转换一下思路。如果能将这两个数区分开来,即分到两组里,再分别与两组里其它数异或,就可以得出这两个数分别是什么了。例如:

4 1 4 6 5 5分为4 1 4 和 6 5 5 两组。分别异或,4 ^ 1 ^ 4=>1,6 ^ 5 ^5=>6。这时,我们就找到了落单的1和6两个数字。

PS:由于分组规则是一定的,那么重复数字一定会分到同一组,例如两个4不可能处于不同组。

难点在于如何制定分组规则,区分开两个落单数字。
联想一下,当我们对奇偶数进行分组时,用最后一位二进制是否为1来作为规则。那么,我们可以得出:通过 & 运算来判断一位数字不同即可分为两组
那么如何找出哪一位不同呢?这就可以用上我们刚才求得的异或结果了。

6 对应的二进制: 110
1 对应的二进制: 001
1 ^ 6 二进制: 111

可以看出,对于6和1这两个数而言,任意一位都可以作为区分的依据,因为它们的每一位都不同。即:001、010、100都可以作为区分规则。例如:
6 & 2(即010)=> 010
1 & 2(即010)=> 000
010这个值称为mask。
那么,我们使用 nums[i] & mask == 0 这个判据,就可以将 6 和 1 分入两组了。C语言中,0为假,非0为真,不是非0即1的概念。因此注意一定是 == 0 ,而不是 != 1。

最后,再将两组的异或结果存入函数返回的数组中即可。

代码如下:

#include <stdio.h>
#include <stdlib.h>

/**
* Note: The returned array must be malloced, assume caller calls free().
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值