问题描述:
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字
解答这题之前,我们先来做相对简单的
如果除了一个数字以外,其他数字都出现了两次,那么如何找到出现一次的数字?
稍微思考一会儿,你就能想出至少一道解法。。。。是不是觉得非常简单,其实!!!实现确实就是那么简单,毕竟条条大路通罗马。
以下是我全部的实现
- 使用Object或则Array 存储
- 先排序,然后在记录
- 异或(leecode官网解答,简直醍醐灌顶)
第一种方式,相信大家都会,我就不说了
第二种方式,思路就是先排序,那么相同的数字一定仅仅挨着一起的
function find(arr) {
arr.sort()
for (let i = 1; i < arr.length; i++) {
const pre = arr[i - 1]
const num = arr[i]
if (pre === num) {
arr[i - 1] = null
arr[i] = null
}
}
return arr.filter((item) => item !== null)
}
能做出来,但是要求:其他数字智能出现2次,出现3 这个方法就挂了。
第三种,看完之后,惊叹:为何自己那么菜逼。
继续写之前我们先来了解两个操作符:
==按位与==(后面解法里面会用到)
对于每一个比特位,只有两个操作数相应的比特位都是1时,结果才为1,否则为0。
==^异或==:
官网解释:对于每一个比特位,当两个操作数相应的比特位有且只有一个1时,结果为1,否则为0。
对于这题你可以理解为:不存在做加法,存在做减法。那么剩下的肯定就只出现了1次(其实我是想说奇数次)
直接上码:
function find(arr) {
for (let i = 1; i < arr.length; i++) {
arr[0] ^= arr[i]
}
return arr[0]
}
看到这里,是不是已经开始兴奋了,顶着熊猫眼的你是不是又能撸码三小时。反正我是到天明了。。。
那么如果有两个只出现过1(奇数)次的数呢?(这两数记为a、b方便写作)做完 ^ 操作最终结果是什么呢?
根据我上面的简单理解:==不存在做加法,存在做减法==,那么就很清楚知道最终结果是:a、b之和。
是不是感觉离成功很近了????然而。。。哈哈哈
解题思路可以去看看官网的:分组异或
都去看官网了,要我干嘛呢?
你猜猜呢...
不知道是否是我太笨了,看完官网解释(三遍以上)和到最终真正理解,居然花了小半上午。对!!一定是官网说的不好,一定是这样的。
感觉官网下面这点说的有瑕疵,或者说容易被误导:
如果我们可以把所有数字分成两组,使得:
两个只出现一次的数字在不同的组中;
相同的数字会被分到相同的组中。
个人觉得:相同数字会被分到相同数组中。这句话,不仔细分析,不看代码执行过程,真的不好理解
当然如果你看一遍就完全了解了,那请受小弟一拜。你语文一定很好!!!
结合官方和自己的理解,对解题思路做了总结和解释:
- 先对所有数字进行一次异或,得到a、b的异或值(ret)。
- 在ret中找到任意为 1 的位,记为 div。
- 根据d对所有的数字进行分组。(其实这一步,做的最最重要的事儿,就是保证了a、b在不同组里面,其余数字就根据 与 div 做 & 操作的结果而定了,本分到哪个组就不清楚了)
- 然后在两组内做异或操作,就能得到结果了
结合代码来分析一下:
// arr = [4, 1, 5, 8, 2, 2, 1, 5]
let ret = 0
for (let i in arr) {
ret ^= arr[i] //得到 a、b两数之和 (步骤1)
}
console.log('两数之和为:', ret) //打印出 12
let div = 1
//得到ret 第一位为1 的值
while ((div & ret) == 0) {
div <<= 1
}
console.log('第一位不为1对应的值为:', div) // div为4,也就是 10,第二位数为1(步骤2)
let a = 0
let b = 0
let a1 = [] //辅助分析执行过程
let b1 = [] //辅助分析执行过程
for (let i in arr) {
if (div & arr[i]) {// 分组 (步骤3)
a1.push(arr[i])
a ^= arr[i]
} else {
b1.push(arr[i])
b ^= arr[i]
}
}
console.log('第一组的数字为:', a1) // [4, 5, 5]
console.log('第二组的数字为:', b1) //[1, 8, 2, 2, 1]
如果把数组改为:[3, 1, 5, 8, 2, 2, 1, 5],那么输出则会使:
- 两数之和为: 11
- 第一位不为1对应的值为: 1
- 第一组的数字为: [3, 1, 5, 1, 5]
- 第二组的数字为: [8, 2, 2]
加上这个过程,你可能能更加理解这道理。
.......
说了这么多废话: 什么解题思路,什么巧妙解题法
都不如:==动手写,去思考,多练习==。(这才是最重要的)
原题来自于Leecode: https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/