题目描述

✅ 645. 错误的集合

image-20260929102034456

题意分析

长度为 $n$ 的数组原本应包含 $1$ 到 $n$ 各一次,现在其中一个数被改成了另一个已有的数。找出重复值与缺失值,并按这个顺序返回;数组不保证有序。

解法:计数数组

核心思路

[!blue]
先按值统计出现次数,再找偏离一次的两个值。 一次替换使原值从出现一次变成零次,目标值从出现一次变成两次,其余值不变。因此频次为 2 的就是重复值,频次为 0 的就是缺失值,两者都唯一。

合法值域是 $[1,n]$,可以直接创建长度为 $n+1$ 的计数数组,让 cnt[v] 表示数字 $v$ 的频次;下标 0 不使用。遍历全部输入后,再扫描 $1$ 到 $n$,分别把频次为 2 和 0 的下标记录为 dup、missing。

缺失判断必须等统计结束后再做,否则暂时没读到的数字也会被误认为缺失。按值计数不依赖输入顺序,也不需要修改数组。最后返回 [dup, missing],顺序由题意规定,与两个值谁大谁小无关。

解题步骤

  1. 创建 n+1 长度的频次数组。
  2. 遍历输入,按数值增加频次。
  3. 扫描 1 到 n,分别记录频次二和零的位置。
  4. 按规定顺序返回两值。

代码实现

class Solution {
    public int[] findErrorNums(int[] nums) {
        int n = nums.length;
        int[] cnt = new int[n + 1];

        for (int x : nums) {
            cnt[x]++;
        }

        int dup = -1;
        int missing = -1;

        // 只扫描合法值域,频次二与零分别对应重复值和缺失值。
        for (int i = 1; i <= n; i++) {
            if (cnt[i] == 2) {
                dup = i;
            } else if (cnt[i] == 0) {
                missing = i;
            }
        }

        return new int[] {
            dup,
            missing
        };
    }
}
func findErrorNums(nums []int) []int {
    n := len(nums)

    count := make([]int, n+1)
    for _, num := range nums {
        count[num]++
    }

    dup, missing := -1, -1

    // 只扫描合法值域,频次二与零分别对应重复值和缺失值。
    for i := 1; i <= n; i++ {
        if count[i] == 2 {
            dup = i
        } else if count[i] == 0 {
            missing = i
        }
    }

    return []int{
        dup,
        missing,
    }
}

复杂度分析

  • 时间复杂度:$O(n)$,统计和查找各一遍。
  • 空间复杂度:$O(n)$,频次数组。

关键点总结

[!green]

  • 值域和数组下标直接对应。
  • 频次统计同时服务重复和缺失两个答案。
  • 缺失值需要在完整统计后判断。

易错点总结

[!yellow]

  • 只开 n 项且直接按值索引:值 n 会越界。
  • 发现重复就立即返回:缺失值尚未确定。
  • 只用实际和减理想和:仅得到两者差,不能独立确定两个值。
  • 交换返回顺序:不符合接口要求。

相似题目

题目 难度 关联与区别
268. 丢失的数字 简单 原题只缺一个数,本题还多一个重复值,需要同时还原两个异常值。
442. 数组中重复的数据 中等 同样可利用1到n的下标映射识别重复,本题还要找缺失位置。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/10619280
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!