二分法解决 260. 只出现一次的数字 III(LeetCode算法题)

题目描述:

给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。

进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?

示例 1:

输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。

示例 2:

输入:nums = [-1,0]
输出:[-1,0]

示例 3:

输入:nums = [0,1]
输出:[1,0]

提示:

    2 <= nums.length <= 3 * 104
    -231 <= nums[i] <= 231 - 1
    除两个只出现一次的整数外,nums 中的其他数字都出现两次

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

分析题目可以得出的有用信息:
只要将不重复的两个数分在不同的两个组中,
根据类似题*136. 只出现一次的数字*,
对每个组所有数进行一次异或操作就可以得出两个数

算法如下:

    首先进行一次排序
    结合折半查找
    当right-left > 1
        将数据折半,注意避免在两个相同的数中间切开
        先取数组的左半边,计算所有数字的异或结果first
            若first结果为 0,则两个数均在右半边,重新查找右半边
            若first结果不为0,取nums的右半边,计算所有数字的异或结果second
                若结果second为0,则两个数均在左半边,重新查找左半边
                若second结果不为0,则两个数分列左右两半,分别为first,second
    当right-left == 1时,left和right的位置就是这两个数

时间复杂度:O(nlogn) 排序 + 查找0 + 折半计算= nlogn + n + nlogn


注意:

    首先要对数组进行一次排序(当然,排序后依次遍历也可以发现单独出现的数,不过我就要用折半。。。之前写出来了折半不舍得放弃。。。)
    分两边的时候要避免在两边分别产生新的不重复的数,nums[mid] == nums[mid + 1]则表明将两个相同的数分开了,采用 mid += 1 解决
    数组中有一个数是0的时候要特殊处理一下,如nums = [0,1,1, 2],分半左边为0会被忽略


class Solution:
    """
    执行用时:60 ms, 在所有 Python3 提交中击败了15.84% 的用户
    内存消耗:15.8 MB, 在所有 Python3 提交中击败了68.32% 的用户
    """
    # 返回nums一段上所有数字的 异或 结果
    def fun_xor(self, nums: List[int], left: int, right: int) -> int:
        return reduce(lambda x, y: x ^ y, nums[left:right+1])

    def fun_zheban(self, nums: List[int], left:int, right:int) -> List[int]:
        if right - left > 1:
            mid = (right + left)//2
            # 恰好折半处产生不重复的数分列两边,即折半处两个数相等,如[1, 2, 2, 3,     3, 4, 4, 5]
            if nums[mid] == nums[mid + 1]:
                mid += 1
            first = self.fun_xor(nums, left, mid)
            if first == 0:
                # 注意 mid+1 避免两次传入 nums[mid]
                # 注意递归调用时,调用自身需要加 return
                return self.fun_zheban(nums, mid + 1, right)
            else:
                # 注意 mid+1 避免两次传入 nums[mid]
                second = self.fun_xor(nums, mid + 1, right)
                if second == 0:
                    return self.fun_zheban(nums, left, mid)
                else:
                    return [first, second]

        else:   # right-left == 1
            return [nums[left], nums[right]]

    def singleNumber(self, nums: List[int]) -> List[int]:
        nums.sort()
        # if 0 in nums:     # 注意,特殊情况是数组中只有一个0,有两个0无所谓
        if nums.count(0) == 1:
            return [0, self.fun_xor(nums, 0, len(nums) - 1)]
        else:
            return self.fun_zheban(nums, 0, len(nums) - 1)

原文连接:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/er-fen-fa-jie-jue-260-zhi-chu-xian-yi-ci-3jpd/

本解非最优,最优题解可以参照官方题解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值