检测一个数组里面出现次数为奇数的数字

函数:

function remOccurOdd(arr) {
    var result = 0;
    for(var value of arr) result = result ^ value;
    return result;
}

算法的核心步骤就是把0和数组内所有元素连续作异或运算,得到的结果就是我们想要的

如设数组为 [7, 9, 6, 4, 9, 4, 7],那么结果就是 0^7^9^6^4^9^4^7 = 6,结果正确,原理解释如下

可以把异或运算看作按位序号划分列,一列一列地求得结果再合并
如序号1的列:    0^1^1^0^0^1^0^1 = 1
同理每列的结果都可以求得
每一列的结果组成的二进制数就是目标,这里结果是00000110即6

因为对每一列而言, 最后结果都是出现次数为奇数的那个数字(0或1), 比如第一列结果为0, 第二列结果为1
对每一列, 异或的最后结果和目标的都是一样的(因为只有目标的数字在一列里面出现次数为奇数),下面进一步解释

对所有情况,把列分割:    

                                      1. 无视第一个数字(因为第一个数字总是0,0^0=0,0^1=1)

                                      2. 接下来第一个数为1份
                                      3. 其余的每俩个数作为一份

如第一列01100101可以划分为(1) (10) (01) (01)

数字要按份进行异或运算,如(1) (10)=>1 ^1^0,(1) (10) (01)=>1^1^0 ^0^1

等下就会发现这样会很方便理解原理

称函数最终结果即出现次数为奇数的那个数为‘目标’

再引入一个概念:‘目前而言出现次数为奇数的数字’为‘累计目标’

比如(1) (10)的累计目标为0,(1) (10) (01)的累计目标为1

那么在最后处理完所有数之后得到的‘累计目标和‘目标’是一样的

我们先来看一个问题:

1. 假设目前累计目标为0,如果后面已经没有数字了,那么这一列的结果就是0

如果后面还有数字,那么接下来的一份必定是(0 0),(0 1),(1 0),(1 1)当中的一种,因为俩位二进制的组合只有这四种情况,那么

对照表,会发现累计目标为0时,与连续的俩个二进制数进行异或运算的结果,结果会是一个累计出现次数为奇数的数字(新的累计目标)

2. 同理假设目前累计目标为1,可以得到表

那么对于任意的累计目标,对连续的俩位二进制数(也就是一份数)进行异或运算得到的结果总是新的累计目标

那么一列数不止划分为一份的话,第一份作为累计目标,然后对后面一份一份进行处理

如:

最终结果总是出现次数为奇数的数字,与目标的对应,对任意合理的二进制串都成立

那么,每一列的异或运算的结果都是和目标的一样的,相当于把目标的位按序号复制到结果对应的位上,那么结果就必定是目标

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值