LeetCode 137: 只出现一次的数字(singleNumber)

难度:simple
本题同时也是《剑指offer 专项突击》的第4题。

problem

https://leetcode.cn/problems/WGki4K/
or
https://leetcode-cn.com/problems/single-number-ii/

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。
示例 1:
输入:nums = [2,2,3,2]
输出:3
示例 2:
输入:nums = [0,1,0,1,0,1,100]
输出:100
提示:
1 <= nums.length <= 3 * 104
-231 <= nums[i] <= 231 - 1
nums 中,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次
进阶:你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

初步解法

很直观的想法,就是用python的字典,有这个键就 对值加1,没有就新增键。最后看那个key的值是1.

class Solution:
    def singleNumber(self, nums) -> int:
        dict= {}
        for i in range(len(nums)):
            if not nums[i] in dict.keys():
                dict[nums[i]]=1
            else:
                dict[nums[i]]+=1
        for k in dict.keys():
            if dict[k]==1:
                return k

优化(可能是蠢化。。。)

考虑另一个问题:

给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 2 次 。请你找出并返回那个只出现了一次的元素。

注意到:

  • 所有数异或(^)自己的结果都是0.
  • 也就是说一个数和自身异或偶数次还是0.
  • 如果一个数组只有某个元素出现奇数次,其余元素出现偶数次,直接对整个数组每个元素异或就能找出出现奇数次的元素。
class Solution:
    def singleNumber(self, nums) -> int:
        res = nums[0]
        for i in range(1,len(nums)):
            res = nums[i] ^ res
        return res

更广泛的:

如果数组中只有一个数字出现m次,其余数字都出现n次。找出唯一出现m次的那个数字。假设m不能被n整除。

所有数字的第i个数位的和能被n整除,那么出现m次的数字的第 i 位为0;否则为 1.

注意到:

  • **一个数二进制表示的右起第 i i i 位可以用
  • ( n u m > > i ) & 1 (num>>i)\&1 (num>>i)&1得到。**
  • eg. 6:‘110’, (6>>2)&1=1,‘110’右移2位是‘…001’,与1与得到1.

这里有个问题是二进制以补码的形式存在,对于负数就变了。

二进制补码表示

比如 -5,补码为:

1111 1111 1111 1111 1111 1111 1111 1011

如果是用上面的式子把每一位存下来,这个32位二进制要再求一次补码才是结果。因为int(''11111111111111111111111111111100'',2)得到4294967292,而不是我们想要的 -4
因此要自己写一下转补码的。

完整代码

class Solution:
    def singleNumber(self, nums) -> int:
        # dict= {}
        # for i in range(len(nums)):
        #     if not nums[i] in dict.keys():
        #         dict[nums[i]]=1
        #     else:
        #         dict[nums[i]]+=1
        # for k in dict.keys():
        #     if dict[k]==1:
        #         return k

        # 只有一个数出现奇数次,其余数出现奇数次,找出这个数
        # res = nums[0]
        # for i in range(1,len(nums)):
        #     res = nums[i] ^ res
        # return res

        '''
        如果数组中只有一个数字出现m次,
        其余数字都出现n次。找出唯一出现m次的那个数字。假设m不能被n整除。
        '''
        res = [0]*32
        for i in range(len(nums)):
            for k in range(32):
                res [k] += (nums[i]>>k)&1

        for k in range(32):
            res[k]=res[k]%3
        res.reverse()
        
        if res[0]==1:
            sign = -1
            for k in range(32):
                if res[k] == 1:
                    res[k] = 0
                else:
                    res[k]=1
        else:
            sign = 1
        # print(res)
        res = ''.join(list(map(str, res)))
        if sign ==-1:
            res = (int('0'+res[1:],2)+1) * sign
        else:
            res = int('0'+res[1:], 2)
        return res

if __name__ =='__main__':
    so = Solution()
    nums = [-2,-2,1,1,4,1,4,4,-4,-2]
    # nums = [2,2,1,1,4,1,4,4,4,2]
    # nums = [-2,-2,3]
    print(so.singleNumber(nums))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值