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

38 阅读3分钟

问题描述

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

要求:

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

这道题有两个要求,一是时间复杂度要求O(n)O(n),二是要求空间尽量使用的小。

很显然的一种做法是开一个数组作为桶记录每一种数出现的次数,再寻找哪个数字只出现了一次。这个办法是可行的,但并不优秀。它符合时间复杂度的要求(如果从桶中提取一个数字的时间复杂度是O(1)O(1)的话)。

不妨想象这样一种情况,就是当每一个数都充分大的时候,比如说数量级都是1e9的时候,这个桶就需要精心设计,比如使用C++ STL中自带的map红黑树,或者自己手写哈希表亦或者是使用自然溢出大法,这都是可行的,但是空间复杂度仍然是O(n)级别的。 而这很有可能不满足要求。

所以我们想到了另外一种解决方案,使用位运算。

众所周知,异或(xor/^)运算满足结合律和交换律。这里不再证明。

而实际上异或有一个相当好的性质,当相同的两个数进行异或的时候,得到的结果是零。

当一个数异或上零时,得到的是这个数本身。

根据异或的这些性质(或者说条件),我们不妨重新审视这道题。 我们不妨假设这两次出现的数已经两两配对好,那么它们异或起来的值分别都是零,这些零异或起来还是零,再与最后一个单独出现的数异或起来,就是那个数本身。

而异或满足交换律,所以这个两两配对的过程我们不需要自行手动完成,已经完成了(

所以这道题就变成了把这些数全部异或起来看最后的数字是多少。

位运算在编写程序的时候往往有着很大的帮助。我们在分析完逻辑之后往往是从人运算的角度去思考计算方式,这些计算的底层逻辑很多都是位运算,比如加减乘除。 实现乘以2除以2我们当然可以直接*2或者/2。 但是从位运算的角度我们还可以将这个数左移或者右移。

如果我没有记错的话,位运算因为是优先级较低的运算,意味着其更加底层,对于C++程序来说它运行的速度会更快。

这些小地方的优化还是值得注意的。 位运算的思想在这道题也有很好的体现,虽然说这道题不难就是了。

以下是核心代码模式下的python代码(谁让当时没有开C++,python都是现学现用的)

def solution(inp):
    # Edit your code here
    unique_number = 0
    for number in inp:
        unique_number ^= number
    return unique_number

if __name__ == "__main__":
    # Add your test cases here
    print(solution([1, 1, 2, 2, 3, 3, 4, 5, 5]) == 4)
    print(solution([0, 1, 0, 1, 2]) == 2)
    print(solution([7, 3, 3, 7, 10]) == 10)