刷题记录之“找单独的数”题目解析|豆包MarsCode AI刷题

75 阅读4分钟

问题描述

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

要求:

  1. 设计一个算法,使其时间复杂度为 O(n),其中 n 是班级的人数。
  2. 尽量减少额外空间的使用,以体现你的算法优化能力。
public class Main {
    public static int solution(int[] inp) {
        int res = 0; 
        for (int num : inp) { 
            res ^= num;  
        }
        return res;  
    }
    public static void main(String[] args) {
        // Add your test cases here
        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}) == 4);
        System.out.println(solution(new int[]{0, 1, 0, 1, 2}) == 2);
    }
}

问题理解

需要在一个整数数组中找到唯一一个出现一次的数字,而其他数字都恰好出现两次。

由于题目要求时间复杂度为 O(n),并且尽量减少额外空间的使用,我们可以考虑使用位运算来解决这个问题。

步骤

  1. 异或运算的性质

    • 异或运算(XOR)有一个重要的性质:a ^ a = 0 和 a ^ 0 = a
    • 这意味着,如果一个数字出现两次,它们异或的结果会是 0。
    • 而唯一出现一次的数字与 0 异或的结果仍然是它本身。
  2. 遍历数组

    • 初始化一个变量 res 为 0。
    • 遍历数组中的每一个数字,将 res 与当前数字进行异或运算。
    • 最终,res 的值就是那个唯一出现一次的数字。

扩展

关于异或运算

在计算机科学中,异或运算是一种基础而又强大的操作。不同于常见的加法、减法等运算,异或(XOR)运算符在处理某类问题时能够展现出其独特的优势。在这篇随笔中,我想分享一些关于异或运算的巧妙算法题,以及它们在解题中的思维启发。

一、异或运算的基本性质

首先,让我们回顾异或运算的几个重要性质:

  1. 交换律:a ⊕ b = b ⊕ a
  2. 结合律:a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c
  3. 自反性:a ⊕ a = 0
  4. 零元:a ⊕ 0 = a

由于这些性质,异或运算在解决某些问题时非常高效。例如,利用自反性,我们可以快速消除相同的元素,留下独特的结果。

二、经典算法题:找到数组中的唯一数

考虑一个简单而经典的问题:给定一个整数数组,其中每个元素都出现两次,唯有一个元素只出现一次,如何有效地找出这个唯一的元素?

解法分析

使用异或运算可以高效地解决此问题。由于相同的数异或等于0,我们可以将所有数进行异或操作,最终剩下的就是那一个唯一的数。

int findUnique(int arr[], int n) {
    int unique = 0;
    for (int i = 0; i < n; i++) {
        unique ^= arr[i];
    }
    return unique;
}

实验结果

对于数组 [2, 2, 3, 4, 4, 3, 5],运行此函数将返回 5。该算法的时间复杂度是 O(n),而空间复杂度是 O(1),无疑是处理此类问题的最佳解。

三、扩展思考:子集的异或和

另一个有趣的问题是计算给定集合中所有子集的异或和。对于大小为 n 的集合,其所有可能子集的个数为 (2^n)。但如果考虑异或的性质,能否用更高效的方法解决?

解法思路

在思考的过程中,发现每个元素在所有子集中出现的次数具有规律。如果某个元素在集合中出现了奇数次,它的影响会保留;如果是偶数次,则影响会被完全抵消。因此,我们只需关注那些出现奇数次的元素。

想象一个程序迭代整个集合,使用异或将所有数字相加,最终留下的就是所求的异或和。

int subsetXOR(int arr[], int n) {
    int result = 0;
    for (int i = 0; i < n; i++) {
        result ^= arr[i];
    }
    return result * (1 << (n - 1)); // 每个元素在 2^(n-1) 个子集中出现
}

实验结果

如果集合为 {1, 2, 3},调用此函数将返回其所有子集的异或和,其结果是 0。这是因为在该集合中,每个元素在其对应的子集中出现的次数互为对称。

四、总结

通过这两道典型的算法题,我深刻感受到了异或运算在算法设计中的重要性和灵活性。异或不仅是一个基本的数学逻辑运算,更是解决复杂问题的一把利器。它的独特性质为我们提供了一种高效的思维方式,让我们在面对计算和逻辑的挑战时,拥有更多元的解法。

在算法的学习和应用中,异或运算的思维启发了我去寻找问题中的规律和简化方案。未来我会继续探索异或运算在更多算法领域的应用,希望能在这一方向上开拓新的视野。