LeetCode 3289.数字小镇中的捣蛋鬼:哈希表O(n)空间 / 位运算O(1)空间

【LetMeFly】3289.数字小镇中的捣蛋鬼:哈希表O(n)空间 / 位运算O(1)空间

力扣题目链接:https://leetcode.cn/problems/the-two-sneaky-numbers-of-digitville/

数字小镇 Digitville 中,存在一个数字列表 nums,其中包含从 0n - 1 的整数。每个数字本应 只出现一次 ,然而,有 两个顽皮的数字额外多出现了一次,使得列表变得比正常情况下更长。

为了恢复 Digitville 的和平,作为小镇中的名侦探,请你找出这两个顽皮的数字。

返回一个长度为 2 的数组,包含这两个数字(顺序任意)。

示例 1:
输入: nums = 0,1,1,0

输出: 0,1

解释:

数字 0 和 1 分别在数组中出现了两次。

示例 2:
输入: nums = 0,3,2,1,3,2

输出: 2,3

解释:

数字 2 和 3 分别在数组中出现了两次。

示例 3:
输入: nums = 7,1,5,4,3,4,6,0,9,5,8,2

输出: 4,5

解释:

数字 4 和 5 分别在数组中出现了两次。

提示:

  • 2 <= n <= 100
  • nums.length == n + 2
  • 0 <= nums[i] < n
  • 输入保证 nums恰好包含两个重复的元素。

解题方法一:哈希表

使用哈希表统计每个数是否出现过。

遍历数组,若一个数已经在哈希表中出现过,则将其加入到答案中;否则将这个数添加到哈希表中。

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( n ) O(n) O(n)

AC代码

C++
cpp 复制代码
/*
 * @LastEditTime: 2025-10-31 22:19:51
 */
class Solution {
public:
    vector<int> getSneakyNumbers(vector<int>& nums) {
        unordered_set<int> se;
        vector<int> ans;
        for (int t : nums) {
            if (se.count(t)) {
                ans.push_back(t);
            } else {
                se.insert(t);
            }
        }
        return ans;
    }
};
Python
python 复制代码
'''
LastEditTime: 2025-10-31 22:21:51
'''
from typing import List

class Solution:
    def getSneakyNumbers(self, nums: List[int]) -> List[int]:
        se = set()
        ans = []
        for t in nums:
            if t in se:
                ans.append(t)
            else:
                se.add(t)
        return ans
Java
java 复制代码
/*
 * @LastEditTime: 2025-10-31 22:27:27
 */
import java.util.Set;
import java.util.HashSet;

class Solution {
    public int[] getSneakyNumbers(int[] nums) {
        int[] ans = new int[2];
        int already = 0;
        Set<Integer> se = new HashSet<>();
        for (int t : nums) {
            if (se.contains(t)) {
                ans[already++] = t;
            } else {
                se.add(t);
            }
        }
        return ans;
    }
}
Go
go 复制代码
/*
 * @LastEditTime: 2025-10-31 22:24:27
 */
package main

func getSneakyNumbers(nums []int) (ans []int) {
    se := map[int]struct{}{}
    for _, t := range nums {
        if _, ok := se[t]; ok {
            ans = append(ans, t)
        } else {
            se[t] = struct{}{}
        }
    }
    return
}
Rust
rust 复制代码
/*
 * @LastEditTime: 2025-10-31 22:39:35
 */
use std::collections::HashSet;

impl Solution {
    pub fn get_sneaky_numbers(nums: Vec<i32>) -> Vec<i32> {
        let mut se = HashSet::new();
        let mut ans = Vec::with_capacity(2);
        for t in nums {
            if se.contains(&t) {
                ans.push(t);
            } else {
                se.insert(t);
            }
        }
        ans
    }
}

解题方法二:位运算

需要用到位运算两个性质:

  1. 异或一个数偶数次,相当于什么都没干
  2. lowbit: x & − x x\&-x x&−x得到的结果是 x x x二进制下最后一个 1 1 1及其后的 0 0 0

知道了这两个性质,我们就可以先计算出 x ⊕ y x\oplus y x⊕y(假设 x x x和 y y y出现了两次)

使用 0 0 0来异或数组中的每一个数然后再从 0 0 0异或到 n − 1 n-1 n−1,那么数组中每个出现了一次的数总计被异或两次,相当于没有被异或;

而 x x x和 y y y总计被异或了3次,也相当于分别被异或了一次。

所以得到的最终结果就是 x ⊕ y x\oplus y x⊕y的值。

接下来拆分出 x x x和 y y y,核心思想是把 x x x和 y y y分成两组。怎么分成两组呢?还记得我们刚刚得到的 x ⊕ y x\oplus y x⊕y吗:

x ≠ y x\neq y x=y所以 x ⊕ y x\oplus y x⊕y一定非零。假设 x ⊕ y = a x\oplus y=a x⊕y=a,那么对于 a a a二进制下非零的位, x x x和 y y y的对应位一定一个为零一个非零。

为了方便,我们可以以 l o w b i t lowbit lowbit为依据,将所有的数分成两组。 0 0 0到 n − 1 n-1 n−1中所有这一位为 1 1 1的数分成一组,所有这一位为 0 0 0的数分成一组。

那么对于两组分别进行类似的运算(异或两次),就能分别得到 x x x和 y y y了。(因为 x x x所在的那一组里只有 x x x出现了两次)

  • 时间复杂度 O ( n ) O(n) O(n)
  • 空间复杂度 O ( 1 ) O(1) O(1)

AC代码

C++
cpp 复制代码
/*
 * @LastEditTime: 2025-10-31 22:49:15
 */
class Solution {
public:
    vector<int> getSneakyNumbers(vector<int>& nums) {
        int xored = 0;
        for (int t : nums) {
            xored ^= t;
        }
        for (int i = 0; i < nums.size() - 2; i++) {
            xored ^= i;
        }
        // now xored is x^y

        int lowbit = xored & -xored;
        int ans1 = 0, ans2 = 0;

        for (int t : nums) {
            if (t & lowbit) {
                ans1 ^= t;
            } else {
                ans2 ^= t;
            }
        }
        for (int i = 0; i < nums.size() - 2; i++) {
            if (i & lowbit) {
                ans1 ^= i;
            } else {
                ans2 ^= i;
            }
        }
        return {ans1, ans2};
    }
};

同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~

千篇源码题解已开源

相关推荐
find1star1 小时前
LeetCode 141:环形链表
java·算法·leetcode·链表
迅翼SwiftWing1 小时前
编队系列(总)|固定翼编队如何实现稳定飞行?通信架构、协同算法与工程约束深度解析
算法·架构
LB21121 小时前
力扣160 21 86
算法·leetcode·职场和发展
小智老师PMP2 小时前
2026深度解析|PMP第八版与NPDP核心侧重点本质区别(管理类证书怎么选)
开发语言·分布式·算法·职场和发展·产品经理
HugoStudio_SWAN2 小时前
洛谷 B4450 / B3867 / B3923 智慧购物、储蓄与做题——从程序到生活
c++·学习·程序人生·算法·生活
a187927218312 小时前
【算法】动态规划第二篇:双序列 DP 三课——继承、计步与断链
算法·leetcode·动态规划·dp·回溯·暴力·算法讲解
江畔柳前堤3 小时前
前台·中台·后台:2026年AI原生时代的架构全景图
开发语言·人工智能·算法·机器学习·架构·scala·ai-native
文心快码BaiduComate4 小时前
2026 OPC x AI Coding 趋势案例交流会圆满落幕:一个人就一支研发团队
算法
ACM-moran4 小时前
条件显化(二分单峰函数+构造数列)
算法·推公式·整数三分
闻缺陷则喜何志丹4 小时前
【栈 运算系统】P3719 [AHOI2017初中组] rexp|普及+
c++·算法··洛谷·运算系统