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

116 阅读3分钟

找单独的数

在算法设计中,我们经常会遇到需要在看似简单的问题中寻找最优解的情况。今天要讨论的这个找单独的数的问题就是一个很好的例子。这个问题乍看起来可能显得平淡无奇,但当我们深入探讨时,会发现其中蕴含着丰富的算法思想和优化空间。让我们一步步深入分析这个问题,探索从基础解法到最优解的演进过程。

问题本质与基础解法

首先,让我们明确问题的本质:在一个整数数组中,除了一个数字只出现一次外,其他所有数字都恰好出现两次。这个特殊的数据特征给了我们很多思考的方向。最直观的解法是使用哈希表来记录每个数字的出现次数:

def find_unique_number_hash(cards):
    counter = {}
    for card in cards:
        counter[card] = counter.get(card, 0) + 1
    
    for num, count in counter.items():
        if count == 1:
            return num

这个解法的思路非常直观:通过两次遍历,第一次统计每个数字的出现次数,第二次找出只出现一次的数字。它的时间复杂度是O(n),但空间复杂度也是O(n)。对于空间敏感的场景,我们需要寻找更优的解法。

排序思路的探索

在寻找优化方案的过程中,我们可能会想到排序。如果将数组排序,相同的数字会相邻,这样就可以通过比较相邻元素来找出单独的数字:

def find_unique_number_sort(cards):
    cards.sort()
    i = 0
    while i < len(cards) - 1:
        if i + 1 < len(cards) and cards[i] == cards[i + 1]:
            i += 2
        else:
            return cards[i]
    return cards[-1]

这个方法虽然清晰易懂,但由于涉及排序,时间复杂度变成了O(n log n),不满足题目要求的O(n)时间复杂度。不过,这个思路启发我们思考:如何在不排序的情况下处理配对的问题?

位运算的巧妙应用

经过深入思考,我们发现可以利用位运算的特性来解决这个问题。异或运算(XOR)有几个关键特性:

  1. 任何数与0异或得到的结果是其本身:a ⊕ 0 = a
  2. 任何数与自身异或得到的结果是0:a ⊕ a = 0
  3. 异或运算满足交换律和结合律:a ⊕ b = b ⊕ a,(a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)

基于这些特性,我们可以设计出一个优雅的解法:

def find_unique_number_xor(cards):
    result = 0
    for card in cards:
        result ^= card
    return result

这个解法的原理是:当我们对数组中的所有元素进行异或运算时,成对出现的数字会因为异或运算的特性而相互抵消(结果为0),最终留下的就是那个只出现一次的数字。这个方法不仅满足了O(n)的时间复杂度要求,而且将空间复杂度优化到了O(1)。

性能与实现细节的权衡

在实际应用中,我们需要根据具体场景在不同实现方式之间做出选择。如果数据规模较小且对内存消耗不敏感,哈希表方案可能更直观易维护。如果追求极致性能且确保输入数据符合要求,位运算方案是最佳选择。