leetcode面试题56 - I. 数组中数字出现的次数

leetcode56.题目描述

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

解题思路:要求时间复杂度为o(n),空间复杂度O(1),如果没有这些要求,这题很简单,直接用set去重,遍历数字用字典统计个数,输出个数为1 的key,结束。显然空间复杂度并不满足。
  别问我为什么会这题,只能告诉你我之前看过,一直觉得位运算的题都很巧妙,没见过确实很难想出来。从本题的初级版本的开始吧。
  一个整型数组 nums 里除一个数字之外,其他数字都出现了两次。要求时间复杂度是O(n),空间复杂度是O(1)。
举个例子,比如输入[1,2,1,2,3]需要输出3,这里需要用到位运算异或的性质和异或的交换性
1 异 或 2 异 或 1 异 或 2 异 或 3 = 1 异 或 1 异 或 2 异 或 2 异 或 3 = 3 1异或2异或1异或2异或3=1异或1异或2异或2异或3=3 12123=11223=3
  这里^符号打出来就报错,用中文代替了,可以看出相同的数异或就变成0了,遍历一次两次的数都能为0,最后剩下一个数字和0异或还是本身。代码如下。这里用到reduce高阶函数,对可迭代对象进行逐个的输入到函数中,然后把上次的结果和当前值输入到lambda函数中去,lambda函数是一个异或函数。

from functools import reduce
def find_onetimenum(nums):
	res = reduce (lambda x,y:x^y, nums)
	return res
	

  有了上面的经验,我们可以将所有的数也遍历异或一次。得到的结果就是剩下的两个次数为1的数a和b,即a^b,光知道这两个数的异或结果,也无法知道a和b分别是多少。
  如果能通过这个结果,把这个数组分为两个数组多好,其中一个数组包含a,和若干次数为2的数;另外一个包含b,和剩下次数为2的数。即可实现下图的效果,最后分别再进行一次遍历异或就能分别得到a,b呀

  首先就,把次数为2分开很简单,我们主需要对数据做一些限制,可以把偶数分这边,奇数分另外一遍,这样不就分开了;但是这样分不能保证a和b能分别分到两个组。目前我们只有ab异或的值,只能从这里出发了,异或的结果能表现出a和b在哪些方面的信息呢,如果异或结果,首位为0,说明a和b的首位相同,如果为1说明a,b首位不同,通过找到a和b,不同的那位,那我们不就可以区分a,b。

  以下图为例子,4和5的异或结果为001,如果把所有数和001求与,结果是不是就两种情况,要么为0,要么为1,结果0的分为一组,1的分为一组,可以完美的把这nums分为两组,而且4和5能分开,次数为2的数也在同一组,因为相同的数对001求与结果肯定相同。

  大概做法就是,对数组遍历异或,先求出a和b异或的结果,找到异或结果里的首位为1的数,加入是第四位,就用1000分别遍历这个数组,能将大数组分为两个数组,再对数组分别遍历异或得到a和b的值。代码如下。
from functools import reduce
class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        #获取nums数组a,b的异或结果
        res = reduce(lambda x,y:x^y,nums)
        h = 1
        #找到res最高位数为1的数
        while((res&h)==0):
            h<<=1
        a = 0
        b = 0
        #分组并异或遍历,得到最终异或
        for i in nums:
            if i&h:
                a ^= i
            else:
                b ^= i
        return [a,b]
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值