算法19-----(位运算)找出数组中出现只出现一次的数

题目:

前三题都可以用集合和来求。【sum(set(nums))*N-sum(nums)】

1、数组中别的数都出现2次,只有一个数出现1次,找出该数。

2、数组中别的数都出现3次,只有一个数出现1次,找出该数。

3、数组中别的数都出现N次,只有一个数出现1次,找出该数。

4、数组中别的数都出现2次,只有两个数只出现1次,找出这两个数。

5、数组中1到1000遍历(顺序打乱),有两个数缺失,求出这两个数。

 解题:

1、(3)第1题相当于第3题N为偶数。解法思路:数组为nums,则数组中所有数异或,最后的值为所求。

def findNum(nums):
  if not nums:
    return -1
i
=0 res=0while i<len(nums): res^=nums[i] return res

2、(3)第2题相当于第3题N为奇数。解法思路:所有数的二进制位相加 mod 3 ,所有模3值不为0的位汇总的数即为所求。

def findNum(nums):
    if not nums:
        return -1
    i=0
    res=0
    bitSum=[0]*4
    while i<4:
        j=0
        while j<len(nums):
            bitSum[i]+=(nums[j]>>i)&1
        res|=(bitSum[i]%3)<<i
        i+=1
    return res

更简单的解法:一个数用来表示出现一次的位,也就是说这个数的为1的位就表示这个位上面的数出现过了一次,比如0x10001,就表示bit[0]和bit[4]就是出现过了一次的位。然后再用一个数表示出现过了两次的位,再用一个数表示出现过了3次的位。只要这个位出现过了3次,我就把这个位拿清除掉,这样剩余的最后出现过一次的位的这个数就是我们要找的数了。

def findNum(nums):
    ones=twos=threes=0
    for i in range(len(nums)):
            twos |= ones & A[i]
            ones ^= A[i]
            threes = ones & twos
            ones &= ~threes
            twos &= ~threes
    return ones

 

第4题:先将数组所有数字异或,最后结果就是两个出现一次的数字相互异或的结果,再将这两个数分别分在两个数组中进行异或。如链接:

https://blog.csdn.net/morewindows/article/details/8214003

    设题目中这两个只出现1次的数字分别为A和B,如果能将A,B分开到二个数组中,那显然符合“异或”解法的关键点了。因此这个题目的关键点就是将A,B分开到二个数组中。由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位都说明A,B在这一位上是不相同的。

      比如int a[] = {1, 1, 3, 5, 2, 2}

      整个数组异或的结果为3^5即 0x0011 ^ 0x0101 = 0x0110

      对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

      a[0] =1  0x0001  第一组

      a[1] =1  0x0001  第一组

      a[2] =3  0x0011  第二组

      a[3] =5  0x0101  第一组

      a[4] =2  0x0010  第二组

      a[5] =2  0x0010  第二组

      第一组有{1, 1, 5},第二组有{3, 2, 3},明显对这二组分别执行“异或”解法就可以得到5和3了。

def findNum(nums):
    if not nums:
        return -1
    temp=0
    ##先所有数异或得到A^B的值,A和B为只出现一次的数
    for item in nums:
        temp^=item
    #找到A和B不同的第一位,A^B的第一个1位。该位设为j位
    j=0
    while j<32:
        if (temp>>j)&1==1:
            break
        j+=1
    A=[]
    B=[]
    #将A、B分到按第j位不同分到不同组A和B
    for num in nums:
        if (num>>j)&1==1:
            A.append(num)
        else:
            B.append(num)
    ##分到的两个组全部都异或
    resA=0
    resB=0
    for itemA in A:
        resA^=itemA
    for itemB in B:
        resB^=itemB
    return resA,resB
nums=[1,1,3,5,2,2]
print(findNum(nums))

第5题:思路1:list(set(range(1,1001))-set(nums)),即两个集合相减

思路2:将nums中相对应的数变为负数,最后两个正数为所求。

 

posted on 2018-06-02 10:48  吱吱了了  阅读(1133)  评论(0编辑  收藏  举报

导航