找单独的数--思考与解析| 豆包MarsCode AI 刷题

32 阅读3分钟

问题描述

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

要求:

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

约束条件

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

首先想到的思路是利用哈希表

思路一:使用哈希表

原理

  • 遍历数组并使用哈希表记录每个数字出现的次数。
  • 遍历哈希表,找到出现次数为 1 的数字。

时间复杂度和空间复杂度

  • 时间复杂度:O(n)(遍历数组一次,哈希表查询 O(1))
  • 空间复杂度:O(n)(额外使用一个哈希表存储所有数字)
#include <vector>
#include <unordered_map>
#include <iostream>
int solution(std::vector<int> cards) {
    std::unordered_map<int, int> count;
    for (int num : cards) {
        count[num]++; // 记录出现次数
    }
    for (const auto& entry : count) {
        if (entry.second == 1) { // 出现次数为 1
            return entry.first;
        }
    }
    return -1; // 如果没有找到(输入保证唯一数字存在)
}

但是空间会超过题目设定,转而思考推数学公式求解。

思路二:数学

原理

  • 假设数组为 nums,有 S1 = sum(nums)(数组所有元素之和)。
  • 计算数组中去重后的数字和 S2 = sum(set(nums))
  • 唯一出现一次的数字为 unique = 2 * S2 - S1

时间复杂度和空间复杂度

  • 时间复杂度:O(n)(遍历数组两次)
  • 空间复杂度:O(n)(额外使用一个集合存储去重的数字)
#include <iostream>
#include <vector>
#include <unordered_set>

int solution(std::vector<int> cards) {
    std::unordered_set<int> uniqueNums;
    int S1 = 0, S2 = 0;
    for (int num : cards) {
        S1 += num; // 累加所有数字
        uniqueNums.insert(num); // 插入集合
    }
    for (int num : uniqueNums) {
        S2 += num; // 累加去重后的数字
    }
    return 2 * S2 - S1; // 计算唯一数字
}

空间还是大的,进而考虑到使用位运算,利用二进制的思想。

思路三:使用 XOR(异或)操作

原理

  1. 异或操作的性质

    • x ^ x = 0(一个数与自身异或结果为 0)。
    • x ^ 0 = x(一个数与 0 异或结果为它本身)。
    • 异或满足 交换律结合律,即 (a ^ b) ^ c = a ^ (b ^ c)
  2. 算法核心

    • 将数组中的所有数字依次异或。两次出现的数字会因为 x ^ x = 0 消去,最终只剩下那个唯一的数字。

时间复杂度和空间复杂度

  • 时间复杂度:O(n)(遍历数组一次)
  • 空间复杂度:O(1)(只需常量存储变量)

XOR 方法图解

假设数组为 [2, 1, 4, 5, 2, 4, 1],唯一的数字是 5

  • 初始:result = 0
  • 逐步异或:
result = 2 ^ 1 = 3
result = 3 ^ 4 = 7
result = 7 ^ 5 = 2
result = 2 ^ 2 = 0
result = 0 ^ 4 = 4
result = 4 ^ 1 = 5

最终:result = 5

#include <iostream>
#include <vector>

int findUniqueNumber(const std::vector<int>& nums) {
    int result = 0;
    for (int num : nums) {
        result ^= num; // 异或操作
    }
    return result;
}