当前位置: 首页 > news >正文

LeetCode算法日记 - Day 18: 只出现一次的数字、只出现一次的数字III

目录

1. 只出现一次的数字

1.1 题目解析

1.2 解法

1.3 代码实现

2. 只出现一次的数字 III

2.1 题目解析

2.2 解法

2.3 代码实现


1. 只出现一次的数字

136. 只出现一次的数字 - 力扣(LeetCode)

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

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

示例 1 :

输入:nums = [2,2,1]

输出:1

示例 2 :

输入:nums = [4,1,2,1,2]

输出:4

示例 3 :

输入:nums = [1]

输出:1

1.1 题目解析

数组中,除了一个数字出现一次,其余数字都出现两次。问题抽象成:如何在 O(N) 时间、O(1) 空间内,从数组里找到。

常规解法
最直观做法:用哈希表记录每个数的出现次数,最后找到出现 1 次的数。

哈希表解法能找到答案,但:

  • 空间复杂度是 O(N),不符合“常量额外空间”的要求。

  • 时间复杂度 O(N),但存储开销大。

思路转折
要想省空间,必须抛弃“存储次数”的思路,转而利用位运算。
注意到:

  • 相同的两个数异或结果是 0。

  • 一个数和 0 异或结果是它自己。
    所以整组数异或,成对的数会被抵消,只剩下那个唯一的数。

这就把问题转化成 全体元素求异或,一步解决。

1.2 解法

利用异或运算性质:

  • a ^ a = 0

  • a ^ 0 = a

  • 异或满足交换律、结合律

所以:nums[0] ^ nums[1] ^ ... ^ nums[n-1] = 只出现一次的那个数

i)初始化结果 ret = 0。

ii)遍历数组,把每个数异或到 ret 上。

iii)遍历结束,ret 就是唯一出现一次的数。

1.3 代码实现

class Solution {public int singleNumber(int[] nums) {int ret = 0;// 遍历数组,逐个异或for (int num : nums) {ret ^= num;}return ret;}
}

2. 只出现一次的数字 III

260. 只出现一次的数字 III - 力扣(LeetCode)

给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

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

示例 1:

输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:

输入:nums = [-1,0]
输出:[-1,0]

示例 3:

输入:nums = [0,1]
输出:[1,0]

提示:

  • 2 <= nums.length <= 3 * 104
  • -231 <= nums[i] <= 231 - 1
  • 除两个只出现一次的整数外,nums 中的其他数字都出现两次

2.1 题目解析

题目本质
数组中有两个数只出现一次,其余数都出现两次。问题抽象为:如何在 O(N) 时间、O(1) 空间下,把两个数从数组里分离出来。

常规解法
最直观想法:用哈希表统计频次,最后挑出出现 1 次的两个数

哈希表能直接解题,但:

  • 空间复杂度 O(N),违背了题目“常量空间”的要求。

  • 频次统计过程额外开销大。

思路转折
既然每个数都出现两次,只有两个例外 → 可以考虑异或:

  • 异或能抵消相同元素。

  • 全部异或后结果就是 a ^ b(a、b 是两个目标数)。

  • 问题变成:如何从 a ^ b 里“拆”出 a 和 b?

关键洞察:

  • a ^ b 二进制表示里必然有至少一位是 1

  • 这一位说明 a、b 在该位上不同:一个是 0,一个是 1。

  • 只要用这一位做分组依据,就能把 a 和 b 分开。

这一步就是利用 mark = Integer.lowestOneBit(a ^ b),取到能区分它们的最低位 1

2.2 解法

i)先整体异或,得到 tmp = a ^ b

ii)找出 tmp 的最低位 1,作为掩码 mark。

  • 性质:mark 可能是 1, 2, 4, 8...,不是固定的 1。

  • (num & mark) 结果要么是 0,要么是 mark 本身。

  • mark = Integer.lowestOneBit(tmp)

iii)遍历数组,按 (num & mark) 是否为 0 分两组,分别异或。

  • 因为 a、b 在这一位上不同,它们必然分到不同组。

  • 如果 (num & mark) == 0 → XOR 进 a 否则 → XOR 进 b。

  • 否则 → XOR 进 b。

  • 组内其它成对数抵消,剩下的就是 a 和 b。

2.3 代码实现

class Solution {public int[] singleNumber(int[] nums) {// 第一步:全体异或,得到 a ^ bint tmp = 0;for (int num : nums) {tmp ^= num;}// 第二步:取最低位的 1 作为区分标志int mark = Integer.lowestOneBit(tmp);// 第三步:分组异或int a = 0, b = 0;for (int num : nums) {if ((num & mark) == 0) {a ^= num;} else {b ^= num;}}return new int[]{a, b};}
}

http://www.dtcms.com/a/342376.html

相关文章:

  • 通信工程学习:什么是Template Matching模版匹配
  • iOS 文件管理全景实战 多工具协同提升开发与调试效率
  • Python笔记 第三方库之Pandas的数据组合与缺失数据处理篇
  • 通信工程学习:什么是Camera Calibration相机标定
  • 1000qps怎么保证系统的高可用
  • abc Reachable Set
  • 基于Nodejs作为服务端,React作为前端框架,axios作为通讯框架,实现滑块验证
  • C++ 学习与 CLion 使用:(四)常量和变量,包括字面常量和符号常量
  • 计算机视觉--opencv(代码详细教程)(三)--图像形态学
  • 【框架篇二】FastAPI路由与请求处理
  • javaweb开发笔记——微头条项目开发
  • 零性能妥协:Gearbox Entertainment 通过 AWS 和 Perforce 实现远程开发革命
  • AWS EC2 实例优化检测工具:完整指南与实践
  • WSL的Ubuntu如何改名字
  • AWS Lambda 高并发场景下的错误处理与监控最佳实践
  • 06高级语言逻辑结构到汇编语言之逻辑结构转换 for (...; ...; ...)
  • 一款更适合 SpringBoot 的API文档新选择(Spring Boot 应用 API 文档)
  • 数据结构:构建一棵AVL树需要多少节点(Height VS Nodes in AVL Trees)
  • Claude Code 已支持【团队版】和【企业版】订阅
  • 解析 C 语言整数类型:超越命名的长度奥秘
  • SWMM排水管网水力、水质建模及海绵城市与水环境中的应用
  • 7. if 条件语句的知识与实践
  • 三层交换机
  • CMake2: CMakeLists.txt的常用命令
  • 5.6 element ui
  • 计算机网络技术-第六章
  • STM32 TIM_CtrlPWMOutputs函数
  • 两种单例模式
  • 分享一个免费开源的网站跟踪分析工具Open-Web-Analytics(和GoogleAnalytics一样)
  • 3D 环形旋转图片轮播(纯html,css,js)