找单独的数 | 豆包MarsCode AI刷题

93 阅读4分钟

找单独的数

问题描述

在一个班级中,每位同学都拿到了一张卡片,上面有一个整数。有趣的是,除了一个数字之外,所有的数字都恰好出现了两次。现在需要你帮助班长小C快速找到那个拿了独特数字卡片的同学手上的数字是什么。
要求:

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力。

测试样例

样例1:

输入:cards = [1, 1, 2, 2, 3, 3, 4, 5, 5]
输出:4
解释:拿到数字 4 的同学是唯一一个没有配对的。

样例2:

输入:cards = [0, 1, 0, 1, 2]
输出:2
解释:数字 2 只出现一次,是独特的卡片。

样例3:

输入:cards = [7, 3, 3, 7, 10]
输出:10
解释:10 是班级中唯一一个不重复的数字卡片。

约束条件

  • 1 ≤ cards.length ≤ 1001
  • 0 ≤ cards[i] ≤ 1000
  • 班级人数为奇数
  • 除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次

解决思路与启发

这道题主要有两种解决思路,第一种解决思路需要采用哈希集(HashSet)的数据结构来进行记录;第二种解决思路则是运用异或运算的性质来得到结果。

1.哈希集(HashSet)

由于哈希集是不包含任何重复元素的无序集合。故我们只需要在哈希集不存在当前数组内数字时添加它,当哈希集存在当前数字时,移除它,最后即可得到那个独一无二的数字。

2.异或运算

采用该解决思路,由于问题中"除了一个数字卡片只出现一次外,其余每个数字卡片都恰好出现两次"这一条件,故我们只需遍历数组的同时,对数组内数字进行异或的累次运算即可。代码如下:

    public static int solution(int[] inp) {
        // Edit your code here
        int ans = 0;
        for(int num:inp){
            ans ^= num;
        }
        return ans;
    }

异或运算

在逻辑学中,逻辑算符异或(exclusive or)是对两个运算元的一种逻辑析取类型,符号为 XOR 或 EOR 或 ⊕(编程语言中常用^)。但与一般的逻辑或不同,异或算符的值为真仅当两个运算元中恰有一个的值为真,而另外一个的值为非真。转化为命题,就是:“两者的值不同。”或“有且仅有一个为真。”

yab
000
110
101
011

异或运算性质即

  • 异或满足交换律和结合律:a^b=b^a (a^b)^c=a^(b^c)
  • 任何数与0异或不变:a^0=a
  • 任何数异或自己为零:a^a=0

异或运算常用的场景有:

1.加密与解密:

异或运算可以用于简单的对称加密算法。一个明文通过与密钥进行异或运算可以得到密文,同样地,密文通过与相同的密钥进行异或运算可以还原为明文。

2.数据校验:

异或运算常用于奇偶校验,例如在网络通信过程中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,接收方可以根据校验位判断接收到的数据是否有误。

3.交换两个变量的值:

不使用临时变量交换两个变量的值:

int x = 2;
int y = 3;
x = x ^ y; // x = (2 ^ 3)
y = x ^ y; // y = (2 ^ 3) ^ 3 = 2
x = x ^ y; // x = (2 ^ 3) ^ 2 = 3

通过异或运算性质即可实现两变量值的交换,但此处存在一个非常隐蔽的陷阱,在进行数组两个位置数字进行交换时(num[n],i,j)当i和j指向同一个数组对应地址时,最终只会得到0的结果,这等价于自己和自己做了三次异或

4.位翻转:

异或运算可以用于翻转特定位,例如翻转二进制数 10100001 的第 6 位,可以将该数与 00100000 进行按位异或运算:10100001 ^ 00100000 = 10000001。

5.数据恢复与备份

异或运算同样可以用于数据的恢复与备份。类如当你有A,B,C这三组数据文件时,我们可以通过异或运算生成一个冗余数据文件D(D = A ^ B ^ C)
如果其中一个数据文件丢失,可以通过剩余的数据文件和冗余数据文件 D 来恢复丢失的数据文件。
比如我们丢失了A数据文件,根据异或运算的逻辑 A = D ^ B ^ C ,即可恢复得到A数据文件。