【LeetCode】Day22

本文探讨了利用位运算解决只出现一次数字的问题,包括哈希表、异或运算在寻找单数和双数解法中的应用,以及针对不同数量元素的优化策略。通过实例解析和代码实现,深入理解在不同场景下如何高效找出数组中只出现一次的元素。
摘要由CSDN通过智能技术生成

问题1:只出现一次的数字

解法1:哈希表/排序/集合方法
使用哈希表来记录每个元素出现次数,进而筛选出只出现一次的元素
遍历数组,若元素首次出现,则加入集合,再次出现则删除该元素,最后集合中剩下的元素为只出现一次的元素。
排序后,对数组遍历,若该位置元素与下一元素相同,则跳过该元素。若不想同则表明其为只出现一次的元素。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        nums = list(sorted(nums))
        i = 0
        while(i<len(nums)-1):
            if nums[i] != nums[i+1]:
                return nums[i]
            else:
                i += 1
                while (nums[i] == nums[i-1]):
                    i+=1
        return nums[-1]

( 上述方法的空间复杂度都为O(n),下面尝试方法对空间复杂度进行优化 )

解法2:位运算
该题可使用异或运算,同时异或运算具有以下性质:

因此对于该数据,共2m+1个元素[a,c,a,b,b],m为出现次数为2的元素数量。基于上述异或运算性质3,数组中所有元素之间的异或可表示为如下形式:

根据上述性质2和性质1,上式和化简成如下形式:

因此,数组中的全部元素的异或运算结果即为数组中只出现一次的数字。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return reduce(lambda x, y: x ^ y, nums)

问题2:只出现一次的数字2

解法1:和上一题类似

解法2:依次确定每一个二进制位
跟上题不同的是,多次出现元素的出现次数为3,因此异或的性质不适用,例a ⨁ \bigoplus a ⨁ \bigoplus a=a。
因此需要寻求其它解决方法,通过观察发现,对于数组中3m+1个元素,除去只出现一次的元素,剩下的元素在每一位上的二进制数之和都为3的倍数(0或3),因此对于任一二进制位,所有元素在该位和除以3的余数就为只出现一次元素在该位的元素值。(0+a % 3 = a,3+a % 3 = a)

需要注意的是,如果使用的语言对「有符号整数类型」和「无符号整数类型」没有区分,那么可能会得到错误的答案。这是因为「有符号整数类型」(即int类型)的第 31个二进制位(即最高位)是补码意义下的符号位,对应着 − 2 31 -2^{31} 231 ,而「无符号整数类型」由于没有符号,第 31个二进制位对应着 2 31 2^{31} 231 。因此在某些语言(例如 python)中需要对最高位进行特殊判断。

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        ans = 0
        for i in range(32):
            total = sum((num >> i) & 1 for num in nums)
            if total % 3:
                # Python 这里对于最高位需要特殊判断
                if i == 31:
                    ans -= (1 << i)
                else:
                    ans |= (1 << i)
        return ans

问题3:只出现一次的数字3

解法:位运算
类似于问题1的解法,但是不同的是该题中数组有2m+2个元素,其中有两个不同的只出现一次的元素。
同样我们采用位运算来求解,对于数组中所有元素求异或结果中任一二进制位,出现两次的2m个元素的总体异或值为0,最终该位结果由剩下的两个不同的元素决定,若在该二进制位值不同则为1,相同则为0。
因此,我们可以根据结果中为1的二进制位对两个元素进行区分,进而对那2m个元素进行划分,例:元素在该二进制位为1的属于A组,为0的属于B组。完成划分后,该问题就等价于在A、B的两个等价于问题1的子问题,由此可以分别在A、B中求出两个不同的number_1和number_2。
那么如何求出这个结果中值为1的二进制位呢?
可以使用位运算 x & -x 取出 x 的二进制表示中最低位那个 1。原理可以参考该链接。
https://blog.csdn.net/oyoung_2012/article/details/79932394

class Solution:
    def singleNumber(self, nums: List[int]) -> List[int]:
        xorsum = 0
        for num in nums:
            xorsum ^= num
        
        lsb = xorsum & (-xorsum)
        type1 = type2 = 0
        for num in nums:
            if num & lsb:
                type1 ^= num
            else:
                type2 ^= num
        
        return [type1, type2]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值