【位运算】只出现一次的数字 II,数电的知识终于用上了!
这是一道位运算的题目,感觉解法比较新奇,分享一下~
问题
https://leetcode-cn.com/problems/single-number-ii/
LeetCode137: 给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。 说明: 你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗? 示例 1: 输入: [2,2,3,2] 输出: 3 示例 2: 输入: [0,1,0,1,0,1,99] 输出: 99
思路
数值解法
数组中除某个元素外,其他的会出现 3 次,举个例子,给定数组:nums=[a, a, a, b, c, c, c],那么有等式 3 (a+b+c)=sum (nums)+2b,那么我们要的结果就是 b=(3 (a+b+c)-sum (nums))/2
class Solution:
def singleNumber(self, nums: List[int]) -> int:
return (3*sum(set(nums))-sum(nums))//2
逻辑电路
该思路源于作者:TankCode
来源:https://leetcode-cn.com/problems/single-number-ii/solution/luo-ji-dian-lu-jiao-du-xiang-xi-fen-xi-gai-ti-si-l/
这道题的引申义就是要让我们找到一种运算方式,使得 a×a×a=0 且 0×a=a×0=a, ×
代表运算方式。
若读者学过数电的话,应该就会反映过来,这不就是类似于真值表找通式的过程吗?
- 思路分析
- 第一时间应该想到的是找到一种逻辑操作,可以满足 1×1×1=0 且 0×1=1×0=1 ,其中 × 为这种新逻辑操作符。根据这个,我们可以想到
- 出现 0 次为 0,出现 1 次为 1,出现 2 次的值无所谓,出现 3 次就又回到 0,也就是说,我们一共需要记录 3 种状态:0 次,1 次,2 次,之后次数都是这三种状态的循环。其实这也就是一个模三运算。
- 记录两个状态需要的是一位二进制 0/1,那么记录三种状态需要的是至少两位二进制,可以是 00, 01, 10, 11,这里我们只需要选其中任意三个状态即可,例如:00,01,10,分别代表 0 次 1 次 2 次。
- 用 00 代表 0 次,01 代表出现 1 次是因为刚好对应数字原本那位上 0 代表 0 次,1 代表 1 次,这样可以方便写程序,不这么选也可以,但最后你自己要有个映射规则。至于 2 次的编码无所谓,10 和 11 都可以,反正最后答案也不要它,只是个中间状态,辅助我们计算的。
- 那么对于输入数字的每一位二进制位,都可以用这三种状态表示。如果再输入一个数字,对于每一位上,我们的操作可以化为:
- 新输入的是 0(即 00),三种状态都维持不变,00→00,01→01,10→10
- 新输入的是 1(即 01),00→01,01→10,10→00
- 求逻辑表达式
设当前状态为 XY,输入为 Z,那么我们可以分别为 X 和 Y 列出真值表
- 对于 Y,转化为逻辑表达式就是(取所有
的行的 X,Y,Z 的最小项,然后 OR 起来)
化简完就是
- 同理也可以得出 X 的逻辑表达式,但是这里有一个 tricky 的地方,不用再去求新的表达式。我们先更新完 Y,然后把 放到逻辑表中
替换原来 Y 的值形成新的逻辑表,这个逻辑表对 X 来说是跟求 Y 的时候的逻辑表是同构的。也就是说先更新 Y 以后,拿
去更新 X 时是可以直接套用刚才求的表达式的,也就是
- 这样就算做完了,把所有数以依次输入,然后不断更新这两个状态,最终,出现 3 次的位都成 0(也就是 00),出现 1 次的位都成了 1(也就是 01)。我们最后直接返回状态 Y 就是要的答案。(另外辅助验证,最后 X 应该为全 0,因为最后所有位的状态要么是 00,即出现 3 次的数的位,要么是 01,即出现 1 次的数的位)
代码
class Solution:
def singleNumber(self, nums: List[int]) -> int:
X,Y=0,0
for Z in nums:
Y=Y^Z & ~X
X=X^Z & ~Y
return Y
而这个写法是上述介绍的直接用了
去计算
,可能有人这里不太明白,其实我们也可以 naive 地推出用旧 Y 去计算
的表达式
class Solution:
def singleNumber(self, nums: List[int]) -> int:
X,Y=0,0
for Z in nums:
Y_new=Y^Z & ~X
X=(X&~Y&~Z)|(~X&Y&Z)
Y=Y_new
return Y
或者下面这个程序,原因是
class Solution:
def singleNumber(self, nums: List[int]) -> int:
X = 0
Y = 0
for Z in nums:
Y_new = ~X&(Y&~Z | ~Y&Z)
X = (X&~Y&~Z | ~X&Y&Z)
Y = Y_new
return Y
- 小程序实现原理解析
- EF 通过DataAnnotations配置属性和类型
- IntelliJ IDEA 中的版本控制介绍(上)
- EF基础知识小记七(拆分实体到多个表以及拆分表到多个实体)
- Android 进程回收之LowMemoryKiller原理篇
- 邮件发送功能开发
- Vuejs和其他前端框架的对比
- 详述 IntelliJ IDEA 中恢复代码的方法
- C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能
- 微信小程序之picker组件
- 详述 IntelliJ IDEA 中恢复代码的方法「进阶篇」
- mac环境下mongodb的安装和使用
- C# 终极基类Object介绍
- EF基础知识小记五(一对多、多对多处理)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 使用 OWIN 作为 ASP.NET Web API 的宿主
- 二进制源码和补码的基础解释
- Spark推荐系统实践
- Xamarin Studio 比 Visual Studio 2013 好用的三个功能
- Scss (Sass) 语法简介
- 如何写一个简单的node.js c++扩展
- 给 c# 程序员的十个重要提示
- 子序列问题
- Spark实现推荐系统中的相似度算法
- iOS 中的 CFBundleShortVersionString 与 CFBundleVersion
- 买卖股票问题合集
- 服务出错优先考虑及排查手册(待续整理)
- Android 沉浸式全屏
- Spark SQL 小文件问题处理
- vue常用的修饰符有哪些?