力扣136.只出现一次的数字-异或和HashMap

问题描述

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

方法一:传统HashMap解法

思路分析

最直观的想法是:统计每个数字出现的次数,然后找出只出现一次的那个数字。

实现代码

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        // 创建HashMap来存储数字及其出现次数
        Map<Integer, Integer> map = new HashMap<>();
        
        // 遍历数组,统计每个数字出现的次数
        for (int num : nums) {
            // 如果map中已经包含这个数字
            if (map.containsKey(num)) {
                // 出现次数加1
                map.put(num, map.get(num) + 1);
            } else {
                // 第一次出现,设为1
                map.put(num, 1);
            }
        }
        
        // 遍历map,找到出现次数为1的数字
        for (Integer key : map.keySet()) {
            if (map.get(key) == 1) {
                return key;
            }
        }
        
        // 理论上不会执行到这里(题目保证有解)
        return -1;
    }
}

复杂度分析

  • 时间复杂度:O(n),需要遍历数组一次,遍历map一次

  • 空间复杂度:O(n),最坏情况下需要存储n/2+1个键值对

优缺点

  • ✅ 思路直观,容易理解

  • ✅ 适用于更一般的情况(如找出出现奇数次/偶数次的数字)

  • ❌ 需要额外的存储空间

  • ❌ 代码相对冗长

方法二:HashMap改进版

思路分析

Java 8引入了一些新的API,可以让我们的代码更加简洁优雅。

实现代码

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        Map<Integer, Integer> map = new HashMap<>();
        
        // 使用getOrDefault简化代码
        for (int num : nums) {
            // 如果num存在,获取当前值加1;如果不存在,用0加1
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        
        // 使用entrySet同时获取key和value,避免多次查找
        for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
            if (entry.getValue() == 1) {
                return entry.getKey();
            }
        }
        
        return -1;
    }
}

关键技术点

1. getOrDefault方法

getOrDefault方法在key存在时返回对应的value,不存在时返回指定的默认值(这里是0)。

2. entrySet遍历
java 复制代码
// 使用entrySet(只需一次遍历)
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
    if (entry.getValue() == 1) {  // 直接获取value
        return entry.getKey();    // 直接获取key
    }
}

优缺点

  • ✅ 代码更简洁

  • ✅ 使用entrySet提升遍历效率

  • ❌ 仍然需要额外的存储空间

方法三:异或运算的魔法

思路分析

这是一个需要点数学思维的解法,利用了异或运算的几个重要性质:

  1. 任何数和0异或等于它本身:a ⊕ 0 = a

  2. 任何数和自身异或等于0:a ⊕ a = 0

  3. 异或运算满足交换律和结合律:a ⊕ b ⊕ a = (a ⊕ a) ⊕ b = 0 ⊕ b = b

实现代码

java 复制代码
class Solution {
    public int singleNumber(int[] nums) {
        int result = 0;
        for (int num : nums) {
            result ^= num;  // 对每个数字进行异或运算
        }
        return result;
    }
}

复杂度分析

  • 时间复杂度:O(n),只需遍历一次数组

  • 空间复杂度:O(1),只使用常数级别的额外空间

优缺点

  • ✅ 时间复杂度最低

  • ✅ 空间复杂度最优

  • ✅ 代码极其简洁

  • ❌ 需要理解异或运算的特性

  • ❌ 仅适用于特定情况(其他数字都出现两次)

三种方法对比

特性 传统HashMap 改进HashMap 异或运算
时间复杂度 O(n) O(n) O(n)
空间复杂度 O(n) O(n) O(1)
代码简洁度 一般 简洁 极简
通用性 高(可处理任意次数) 高(可处理任意次数) 低(只适用于两次)
思维难度

拓展思考

如果其他数字出现三次,只有一个出现一次?

这时候异或运算就不适用了,但可以使用HashMap或位运算的扩展解法。

如果有两个只出现一次的数字?

这需要更巧妙的位运算技巧,可以通过分组异或来解决。

总结

这道题目看似简单,却蕴含了丰富的算法思想:

  1. HashMap解法:体现了最直接的"计数"思维,是解决频率统计问题的通用方法

  2. API优化:展示了如何利用语言特性写出更优雅、高效的代码

  3. 异或解法:揭示了数学思维在算法中的强大威力

在实际开发中,我们通常选择HashMap解法,因为它通用且易于理解和维护。但在面试或竞赛中,异或解法往往更受青睐,因为它展示了候选人对问题的深入理解。


希望这篇文章能帮助你更好地理解不同的解题思路。记住,没有最好的算法,只有最适合场景的算法。

思考题:如果题目改为"除了一个数字出现一次外,其他数字都出现三次",你能想到几种解法?

相关推荐
薪火铺子14 小时前
OAuth2 + JWT 微服务认证方案深度解析
java·运维·微服务
diangedan15 小时前
Android冻屏
android·java
曦樂~16 小时前
Cpp多线程
算法
abcnull1 天前
用javaparser做精准测试
java·ast·静态代码分析·精准测试·javaparser
叶小鸡1 天前
Java 篇-项目实战-苍穹外卖-笔记汇总
java·开发语言·笔记
AI人工智能+电脑小能手1 天前
【大白话说Java面试题】【Java基础篇】第22题:HashMap 和 HashSet 有哪些区别
java·开发语言·哈希算法·散列表·hash
昵称小白1 天前
复杂度分析方法
算法
juniperhan1 天前
Flink 系列第21篇:Flink SQL 函数与 UDF 全解读:类型推导、开发要点与 Module 扩展
java·大数据·数据仓库·分布式·sql·flink
科研前沿1 天前
2026 数字孪生前沿科技:全景迭代报告 —— 镜像视界生成式孪生(Generative DT)技术白皮书
大数据·人工智能·科技·算法·音视频·空间计算
ID_180079054731 天前
Python 实现亚马逊商品详情 API 数据准确性校验(极简可用 + JSON 参考)
java·python·json