LeetCode100天Day8-缺失数字与只出现一次的数字

LeetCode100天Day8-缺失数字与只出现一次的数字:排序与找数技巧

摘要:本文详细解析了LeetCode中两道查找类题目------"缺失数字"和"只出现一次的数字"。通过排序后逐个比较的方法找缺失数字,以及排序后通过索引关系找只出现一次的元素,帮助读者掌握排序在查找问题中的应用。

目录

文章目录

  • LeetCode100天Day8-缺失数字与只出现一次的数字:排序与找数技巧
    • 目录
    • [1. 缺失数字(Missing Number)](#1. 缺失数字(Missing Number))
      • [1.1 题目描述](#1.1 题目描述)
      • [1.2 解题思路](#1.2 解题思路)
      • [1.3 代码实现](#1.3 代码实现)
      • [1.4 代码逐行解释](#1.4 代码逐行解释)
      • [1.5 执行流程详解](#1.5 执行流程详解)
      • [1.6 算法图解](#1.6 算法图解)
      • [1.7 复杂度分析](#1.7 复杂度分析)
      • [1.8 边界情况](#1.8 边界情况)
    • [2. 只出现一次的数字(Single Number)](#2. 只出现一次的数字(Single Number))
    • [3. 两题对比与总结](#3. 两题对比与总结)
      • [3.1 共同点](#3.1 共同点)
      • [3.2 排序在查找中的应用](#3.2 排序在查找中的应用)
      • [3.3 索引与值的关系](#3.3 索引与值的关系)
      • [3.4 查找算法对比](#3.4 查找算法对比)
    • [4. 总结](#4. 总结)
    • 参考资源
    • 文章标签

1. 缺失数字(Missing Number)

1.1 题目描述

给定一个包含 [0, n]n 个数的数组 nums,找出 [0, n] 这个范围内没有出现在数组中的那个数。

示例 1

复制代码
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。
2 是丢失的数字,因为它没有出现在 nums 中。

示例 2

复制代码
输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。
2 是丢失的数字,因为它没有出现在 nums 中。

示例 3

复制代码
输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。
8 是丢失的数字,因为它没有出现在 nums 中。

1.2 解题思路

这道题的核心思想是:将数组排序后,逐个检查每个位置的值是否等于其索引。

解题步骤

  1. 对数组进行排序
  2. 从0开始遍历,检查每个位置
  3. 如果当前值不等于索引,说明这个索引就是缺失的数字
  4. 如果遍历完都没找到,说明缺失的是最后一个数

1.3 代码实现

java 复制代码
import java.util.*;

class Solution {
    public int missingNumber(int[] nums) {
        Arrays.sort(nums);
        int temp = 0;

        for(int i = 0; i < nums.length; i++){
            if(temp != nums[i]){
                return temp;
            }
            temp++;
        }

        return temp;
    }
}

1.4 代码逐行解释

第一部分:排序
java 复制代码
Arrays.sort(nums);

排序作用

排序前 排序后
[3,0,1] [0,1,3]
[9,6,4,2,3,5,7,0,1] [0,1,2,3,4,5,6,7,9]

排序后,元素按从小到大排列,缺失的数字会在数组中形成"断层"。

第二部分:查找缺失数字
java 复制代码
int temp = 0;
for(int i = 0; i < nums.length; i++){
    if(temp != nums[i]){
        return temp;
    }
    temp++;
}

查找逻辑

复制代码
期望:[0, 1, 2, 3, ..., n]
实际:[nums[0], nums[1], nums[2], ..., nums[n-1]]

逐个比较:
位置0:期望0,实际nums[0]
位置1:期望1,实际nums[1]
位置2:期望2,实际nums[2]
...

temp != nums[i]时,说明temp这个数字在数组中缺失。

第三部分:返回结果
java 复制代码
return temp;

1.5 执行流程详解

示例1nums = [3,0,1]

复制代码
步骤1:排序
排序后:[0, 1, 3]

步骤2:查找
i=0: temp=0, nums[0]=0, 相等,temp++ → 1
i=1: temp=1, nums[1]=1, 相等,temp++ → 2
i=2: temp=2, nums[2]=3, 不相等,返回 2

输出:2

示例2nums = [0,1]

复制代码
步骤1:排序
排序后:[0, 1]

步骤2:查找
i=0: temp=0, nums[0]=0, 相等,temp++ → 1
i=1: temp=1, nums[1]=1, 相等,temp++ → 2

循环结束,返回 temp = 2

输出:2

示例3nums = [9,6,4,2,3,5,7,0,1]

复制代码
步骤1:排序
排序后:[0, 1, 2, 3, 4, 5, 6, 7, 9]

步骤2:查找
i=0: temp=0, nums[0]=0, 相等,temp++ → 1
i=1: temp=1, nums[1]=1, 相等,temp++ → 2
i=2: temp=2, nums[2]=2, 相等,temp++ → 3
i=3: temp=3, nums[3]=3, 相等,temp++ → 4
i=4: temp=4, nums[4]=4, 相等,temp++ → 5
i=5: temp=5, nums[5]=5, 相等,temp++ → 6
i=6: temp=6, nums[6]=6, 相等,temp++ → 7
i=7: temp=7, nums[7]=7, 相等,temp++ → 8
i=8: temp=8, nums[8]=9, 不相等,返回 8

输出:8

1.6 算法图解

复制代码
示例:nums = [0, 1, 3]

索引:  0   1   2
值:    0   1   3
期望:  0   1   2
        ↓   ↓   ↓
比较:  =   =   ≠
                ↑
            缺失的数字

结果:返回 2

1.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(n log n) 排序O(n log n) + 遍历O(n)
空间复杂度 O(1) Java的Arrays.sort使用原地排序

1.8 边界情况

输入 说明 输出
[0] 缺失1 1
[1] 缺失0 0
[0,1] 缺失2 2
[1,2] 缺失0 0

2. 只出现一次的数字(Single Number)

2.1 题目描述

给你一个非空整数数组 nums,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。

示例 1

复制代码
输入:nums = [2,2,1]
输出:1

示例 2

复制代码
输入:nums = [4,1,2,1,2]
输出:4

示例 3

复制代码
输入:nums = [1]
输出:1

2.2 解题思路

通过排序后,相同的元素会相邻。利用这一特性,可以逐对检查元素:

解题思路

  1. 对数组排序
  2. 从索引0开始,每次检查两个元素(i和i+1)
  3. 如果nums[i] != nums[i+1],说明nums[i]是只出现一次的元素
  4. 如果遍历完偶数索引都没找到,说明最后一个元素是答案

2.3 代码实现

java 复制代码
import java.util.*;

class Solution {
    public int singleNumber(int[] nums) {
        Arrays.sort(nums);

        for(int i = 0; i < nums.length; i++){
            if(i % 2 == 1){
                // 奇数索引,应该与前一个元素相等
                if(nums[i] != nums[i-1]){
                    return nums[i-1];
                }
            }
        }

        return nums[nums.length-1];
    }
}

2.4 代码逐行解释

第一部分:排序
java 复制代码
Arrays.sort(nums);

排序效果

排序前 排序后
[2,2,1] [1,2,2]
[4,1,2,1,2] [1,1,2,2,4]

排序后,相同的元素会相邻。

第二部分:遍历查找
java 复制代码
for(int i = 0; i < nums.length; i++){
    if(i % 2 == 1){
        if(nums[i] != nums[i-1]){
            return nums[i-1];
        }
    }
}

查找逻辑

复制代码
正常情况(每对元素相同):
索引: 0   1   2   3   4
值:  [a,  a,  b,  b,  c]
       ↑   ↑   ↑   ↑   ↑
       偶  奇  偶  奇  偶

检查逻辑:
- 在奇数索引(1,3,5,...)检查是否与前一个偶数索引相等
- 如果不相等,说明前一个元素是单个的
第三部分:返回最后一个元素
java 复制代码
return nums[nums.length-1];

如果所有成对的元素都匹配,说明最后一个元素是单个的。

2.5 执行流程详解

示例1nums = [2,2,1]

复制代码
步骤1:排序
排序后:[1, 2, 2]

步骤2:查找
i=0: i%2=0(偶数),跳过检查
i=1: i%2=1(奇数),检查nums[1]与nums[0]
     nums[1]=2, nums[0]=1
     不相等,返回 nums[0] = 1

输出:1

示例2nums = [4,1,2,1,2]

复制代码
步骤1:排序
排序后:[1, 1, 2, 2, 4]

步骤2:查找
i=0: i%2=0,跳过
i=1: i%2=1,检查nums[1]与nums[0]
     nums[1]=1, nums[0]=1
     相等,继续
i=2: i%2=0,跳过
i=3: i%2=1,检查nums[3]与nums[2]
     nums[3]=2, nums[2]=2
     相等,继续
i=4: i%2=0,跳过

循环结束,返回 nums[4] = 4

输出:4

示例3nums = [1]

复制代码
步骤1:排序
排序后:[1]

步骤2:查找
i=0: i%2=0,跳过

循环结束,返回 nums[0] = 1

输出:1

2.6 算法图解

复制代码
情况1:单个元素在前面
排序后:[1, 2, 2, 3, 3]
索引:   0  1  2  3  4
         ↑  ↑
        不相等
         ↑
    返回 nums[0] = 1

情况2:单个元素在最后
排序后:[1, 1, 2, 2, 3]
索引:   0  1  2  3  4
                    ↑
                都匹配
                    ↑
            返回 nums[4] = 3

2.7 复杂度分析

分析维度 复杂度 说明
时间复杂度 O(n log n) 排序O(n log n) + 遍历O(n)
空间复杂度 O(1) 原地排序

2.8 边界情况

输入 说明 输出
[1] 单个元素 1
[1,1,2] 单个在最后 2
[2,1,1] 单个在最前 2
[1,2,1,2,3] 单个在中间 3

3. 两题对比与总结

3.1 共同点

对比项 缺失数字 只出现一次的数字
核心技巧 排序后比较 排序后比较
时间复杂度 O(n log n) O(n log n)
空间复杂度 O(1) O(1)
排序作用 使元素有序 使相同元素相邻

3.2 排序在查找中的应用

排序的作用

  1. 使元素有序,便于逐个比较
  2. 使相同元素相邻,便于成对检查
  3. 简化查找逻辑,降低算法复杂度

排序方法

java 复制代码
// 1. Arrays.sort(快速排序)
Arrays.sort(array);

// 2. Arrays.sort(指定范围)
Arrays.sort(array, fromIndex, toIndex);

// 3. Collections.sort(用于List)
Collections.sort(list);

3.3 索引与值的关系

缺失数字

java 复制代码
// 期望:nums[i] == i
// 实际:如果不等,i就是缺失的数字
for(int i = 0; i < nums.length; i++){
    if(nums[i] != i){
        return i;
    }
}

只出现一次的数字

java 复制代码
// 期望:nums[i] == nums[i-1](i为奇数)
// 实际:如果不等,nums[i-1]就是单个的
for(int i = 0; i < nums.length; i++){
    if(i % 2 == 1 && nums[i] != nums[i-1]){
        return nums[i-1];
    }
}

3.4 查找算法对比

算法 时间复杂度 是否需要排序
线性查找 O(n)
二分查找 O(log n)
排序后查找 O(n log n)

对于这两道题:

  • 缺失数字可以用数学公式O(n)解决:sum(0到n) - sum(nums)
  • 只出现一次的数字可以用异或O(n)解决

4. 总结

今天我们学习了两道查找类题目:

  1. 缺失数字:通过排序后逐个比较,找到与索引不匹配的数字
  2. 只出现一次的数字:通过排序后成对检查,找到不成对的元素

核心收获

  • 排序是解决查找问题的重要工具
  • 排序后的数组具有很多有用的性质
  • 利用索引与值的关系可以简化问题
  • 奇偶索引在成对检查中很有用

进阶思考

  1. 缺失数字能否用O(n)时间和O(1)空间解决?(提示:数学公式或异或)
  2. 只出现一次的数字能否不用排序,直接用异或解决?
  3. 如果其他元素出现3次,只有一个出现1次,该如何解决?

参考资源

文章标签

#LeetCode #算法 #Java #排序 #数组

喜欢这篇文章吗?别忘了点赞、收藏和分享!你的支持是我创作的最大动力!

相关推荐
梭七y3 小时前
【力扣hot100题】(115)缺失的第一个正数
数据结构·算法·leetcode
Coder_Boy_3 小时前
基于SpringAI企业级智能教学考试平台智能作业模块全业务闭环方案
java·人工智能·spring·spring cloud
嵌入式进阶行者3 小时前
【算法】回溯算法的基本原理与实例:华为OD机考双机位A卷 - 乘坐保密电梯
c++·算法
Geoking.3 小时前
【Java】深入理解 Java 枚举(Enum)
java·开发语言
McGrady-1753 小时前
portal 在scene graph 中怎么生成?
算法·机器人
像风一样的男人@3 小时前
python --生成ico图标
java·python·spring
zhaokuner3 小时前
06-聚合与一致性边界-DDD领域驱动设计
java·开发语言·设计模式·架构
川西胖墩墩3 小时前
智能体在科研辅助中的自动化实验设计
人工智能·算法
ouliten3 小时前
石子合并模型
c++·算法