算法题:从数组找数字(网易2017校园招聘) 2017-10-10 算法爱好者 (点击上方公众号,可快速关注) 给定一个数组,除了一个数出现 1 次之外,其余数都出现 3 次。找出出现一次的数。

算法题:从数组找数字(网易2017校园招聘)

 

给定一个数组,除了一个数出现 1 次之外,其余数都出现 3 次。找出出现一次的数。如:{1, 2, 1, 2, 1, 2, 7}, 找出 7。

 

格式:

 

第一行输入一个数n,代表数组的长度,接下来一行输入数组A[n],(输入的数组必须满足问题描述的要求),最后输出只出现一次的数。

 

要求:

 

你的算法只能是线性时间的复杂度,并且不能使用额外的空间哦~

 

样例输入

4
0 0 0 5

样例输出

5

代码如下:

#include<iostream>
using namespace std;
int main(){
    int nums[10]={1,3,1,4,4,3,17,4,3,1};
    int H=0,L=0,i,n=nums.size(),newH,newL;
    for(i=0;i<n;++i){
        newH=(nums[i] & ~H & L)|(~nums[i] & H & ~L);
        newL=(nums[i] & ~H & ~L)|(~nums[i] & ~H & L);
        H=newH;
        L=newL;
    }
    cout<<L<<endl;
}

原理是这样的,采用二进制的思想,仅仅有一个数出现一次,那么就意味着,把所有的数转换成二进制进行对应位累加,凡是得到的某些位的和不是3的倍数,即位3*n+1时[因为题目说明一个数出现一次,其他数均出现三次,所以绝对不可能出现二进制加和后某些位为3*n+2的情况],就一定时属于那个只出现一次的数,用位对应的权值进行累加即可。现在的问题是空间复杂度要求是O(1)的,那么用数组来存储二进制位并做权值累加就行不通了。但是换个思路,int本身就是32位的,是否可以不用数组而只用少许几个int变量呢,答案是肯定的。

首先,这些对应位累加后的结果要么是3*n,要么是3*n+1,因此我们可以只记录三个状态即可,到3即退回到0状态。因此引入两个变量H,L(两个二进制位可以表示四个状态).

那么我们现在对数X,看作是X_{n}X_{n-1}...X_{1},H看作是H_{n}H_{n-1}...H_{1}1,L是L_{n}L_{n-1}...L_{1}

那么对于X,H,L的第n位,用H、L的两个第i位记录当前数据进行二进制上的数累加时,该位的1的个数。

举个栗子说明一下什么叫做二进制累加吧。现在有一个数组,10,3,3,3,写成二进制7=1010,3=11,纵向堆叠:

1010

0011

0011

0011

再累加起来,就是1043,那么因为1和4不是3的倍数,并且由于这个多余的数只出现一次,他的二进制位和其他数的二进制位的累加的数量必定是3*n+1的,所以说,这个1和4是中的一个1是属于那个多余的数的,分析下来这题的意思就是让你找属于那个多余的数的1。用两个二进制表示三个状态,fx表示要计算的数在二进制下的第i位的值,H,L也表示他们各自在二进制下的第i位的值,~表示取反,H'和L'表示运算后应该得到的值。[~H、~L是为了最后写真值逻辑表达式时候辅助使用的,在实际看数位的状态转移时没必要看]

数位转移的状态转换表:

nums[i]HLH'L'
00000
10001
00101
10110
01010
11000

再来说一次,我们只考虑把数按照二进制表示后的第i位,然后用H和L的第i位的两个二进制数来表示这个位是否是只出现一次的数提供的一位。将所有数变换成二进制后,累加在一起,那么如果二进制下第i个位置上的累加数量是3*n,那这个二进制位对应的权值就不会包含在那个只出现一次的数之内,换句话说,这个只出现一次的数转换成二进制后,第i位的二进制值为0。

但是本身二进制的一位只能表示两种状态,因此,我们用两位二进制状态表示即可,所以,我们用两个数H,L表示,现在只考虑其中的第i位,为了表示三种状态,二进制的状态转移方式应该是00->01->10->00,所以我们画出如上的转换表,根据这张表写逻辑表达式即可。我们用H表示3种状态在二进制下的高位,L表示低位。而由于3*n+1的位置的末状态是01的,那么可以知道这个表示属于只出现一次的数的权值的1是落在L上的[用于表示多余的01状态,其低位的1是落在低位上的],因此结果就是L。

 

写真值表达式的辅助表格:

nums[i]HL~H~LH'L'
0001100
1001101
0011001
1011010
0100110
1100100

 

由之前的推论可知,HL最终表示的数只有3和1,而因为对应位如果为3则不是我们需要求的元素,我们把HL的值模3,实际上H,L第i位二进制上的情况到最后的状态就只有00和01,那么很明显的,最后的结果全部落在L上,所以L的结果值就是需要求的元素。

现在只需要得到H,L每次的计算公式即可。对应于上述表格写出真值逻辑表达式[要计算H就看H‘=1的情况并且让参与运算的三个nums[i],H,L都取值为1后相互做与运算,计算L同理],即:

        newH=(nums[i] & ~H & L)|(~nums[i] & H & ~L);
        newL=(nums[i] & ~H & ~L)|(~nums[i] & ~H & L);
        H=newH;
        L=newL;

如果不用临时变量的话,会在第一步就把H给改变,这样在计算L的时候使用的H是已经更新的H。

(注:运算优先级 ~ >  & > | ),得到结果。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值