Offer 56 - I. 数组中数字出现的次数 (位运算-中等)

Offer 56 - I. 数组中数字出现的次数

题目:一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

          示例 1:     输入:nums = [4,1,4,6]      输出:[1,6] 或 [6,1]

思路1 :初始化一个集合,遍历判断新的元素是否在集合中,如果不在就添加,在则删除。最后剩下的就是一出现过一次的。

问题:时间复杂度是O(n),空间复杂度是O(n)。    空间复杂度不满足要求

def singleNumbers(nums):
    repeat = set()
    for i in range(len(nums)):
        if nums[i] not in repeat:
            repeat.add(nums[i])
        else:
            repeat.remove(nums[i])
    return list(repeat)

因为要求空间复杂度是O(1),所以考虑位运算。

思路2:利用位运算异或和与   具体看代码中分析过程

复杂度:时间复杂度是O(n),空间复杂度是O(1)

PS:① 位中的&运算 具有位过滤作用             ② 3^4=4  3^3-0

'''
    思路2:
        解决找到一个数组只有出现过一次的数字
        ⭐ python位运算中的按位异或运算符:  两个对应的二进位相异时为1  相同时为0      故  a ^ a = 0   0 ^ b = b        
        ⭐ 但是本题的难点是nums中有两个只出现过一次的数字  但是异或只能解决求一个数字。 故问题转移为如何将这两个数字分到不同的子数组? 然后再执行异或操作
        如何将两个数字以及众多对数字分到不同的子数组
        ⭐ ① 针对不同数字对: 首先,我们并不在乎哪几个数字对必须在一起,但是一对数字必须在一起。我们如何做到让所有数字对分成两派(可以想到0和1 0或非0),且相同两个数字对分到一起呢? 
               按位与其实是有过滤作用,即只要和1相与保持原值  和 0相与 则变0。如 有两对数值分别为  0 1 1 0、0 1 1 0  和  1 0 0 1、 1 0 0 1  分别和 0 1 0 0 只有一位1的相与
               会得到  0 1 0 0  和 0 0 0 0 两个答案.
        ⭐ ② 针对两个只出现过一次的数字: 根据 ①的经验,我们选取的只有一位1的二进制数只要可以让两个数分到两个不同子数组中即可。 如 两个数分别是 1 0 1 1 和 0 1 1 1 可以选取 0 1 0 0或者
               1 0 0 0 二者的结果都是不同的。所以我们可以求得第一个不同位的位置 从而来设定来将所有值分组的数m。
    复杂度: 时间复杂度是O(n),空间复杂度是O(1)
'''
# def singleNumbers2(nums):

def singleNumbers2(nums):
    x, y, n, m = 0, 0, 0, 1
    for num in nums:         # 1. 遍历异或
        n ^= num
    while n & m == 0:        # 2. 循环左移,计算 m   0 1 0 1    &  0001
        m <<= 1
    for num in nums:         # 3. 遍历 nums 分组
        if num & m:
            x ^= num # 4. 当 num & m != 0
        else:
            y ^= num       # 4. 当 num & m == 0
    return x, y              # 5. 返回出现一次的数字

路虽远,行则将至。事虽难,做则必成 。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值