豆包MarsCode AI 刷题

123 阅读3分钟

数组中唯一的数

问题及要求

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

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

思考过程

拿到题目第一眼,很自然地想到HashMap:用一个HashMap<Integer, Integer>来记录各数字出现的次数,遍历一遍数组统计次数,同时可以得到出现次数最少的那个数,时间复杂度O(n),空间复杂度O(n)(大概吧。如果考虑散列冲突的话,主要还是看散列函数,但是这个归Java管了),勉强达到最低要求“时间复杂度O(n)”
再看一遍要求,嗯,这个语气就说明空间上肯定是有优化的余地啊,再优化的话,大概率就得优化成O(1)了,怎么用常数个变量找出出现次数最少的那个?这时候注意到一个很强的条件:其余数字均出现两次。一个变量用来统计次数恐怕不太现实,那用这个变量来表示这些数字的一些关系呢?
首先想到的是求和,成对的那些求和后肯定是偶数了,那和的奇偶性就和单个的那个数相同,但这只能确定奇偶性啊。
中间卡了很久...
确定了奇偶性,就确定了二进制的末位,要是每一位都能确定就好了。想着想着,猛然想到了平时几乎不用的位运算(平时就写个前后端啥的,当然几乎用不到,连Java位运算的符号都是上网现查的):异或运算相同为0不同为1,计组老师的口诀突然浮现在我的脑海里——成对的那些数,特定一位上的值一异或就两两抵消了,只剩下单独那个数在这一位上的取值,这样以来不就直接确定了所有位的取值了吗?(异或运算满足交换律和结合律,结合近世代数讲的结论,这个运算可以随意改变次序、随意增减括号而不改变运算结果)
说实话我个人感觉这道题的异或法蛮难想的,我也是卡了挺久,但是看网友说很简单,只能说我太菜了罢

代码

最后附上代码,不得不说代码量小得离谱

public class Main {
    public static int solution(int[] cards) {
        int ret = 0;
        for (int c :
                cards) {
            ret = ret ^ c;
        }
        return ret;
    }

    public static void main(String[] args) {
        System.out.println(solution(new int[]{1, 1, 2, 2, 3, 3, 4, 5, 5}));
        System.out.println(solution(new int[]{0, 1, 0, 1, 2}));
    }
}