2020-08-20

本题和主站137 是一样的. 除了这个,主站还有136和260。 总共加起来本系列一共三道题。 三道题全部都是位运算的套路,如果你想练习位运算的话,不要错过哦~~

异或的性质
两个数字异或的结果a^b是将 a 和 b 的二进制每一位进行运算,得出的数字。 运算的逻辑是
如果同一位的数字相同则为 0,不同则为 1

异或的规律
任何数和本身异或则为0
任何数和 0 异或是本身

136 只出现一次的数字 除一个数字出现一次,其他都出现了两次,让我们找到出现一次 的数
执行一次全员异或即可

def singleNumber(self, nums:List[int]) -> int:
	single_number = 0 #初始值为0,因为0异或任何数都是任何数本身
	for num in nums:
		single_number ^= num
	return single_number

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

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

思路:任何数和本身异或都为0,可以将两个不同的数字分成A和B两组
分组满足的两个条件:
1 两个独特的数字分成不同的组
2 相同的数字分成相同的组
这样每一组的数据进行异或即可得到那两个数字

假设全员异或后的值ret,答案为a,b,有ret=a^b。只需要在ret的二进制里面,找到一个为1的位p(使用lowbit)。显然a和b在p位不同(a!=b则ab至少有一位不同,则一定存在p位)。只需要再次遍历,按照p位01把整体分成两组。可以证明:①相同的数字一定在一组②a和b在不同的分组。组内异或即可找出a和b(其实只要找出a或b

func twoNumber(_ nums:[Int]) -> [Int] {
	var res = 0, a = 0, b = 0
	for num in nums {
		res ^= num
	}
	var h = 1  // 找到第一位不是0的
	while (res & h) == 0 {
		h <<= 1
	}
	for num in nums {
	    // 根据该位是否为0, 将其分为两个数组
		if (h & num) == 0  {
			a ^= num
		}else {
			b ^= num
		}
	}
	return [a, b]
}

剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
限制:
1 <= nums.length <= 10000
1 <= nums[i] < 2^31

  def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for i in range(32):
            cnt = 0  # 记录当前 bit 有多少个1
            bit = 1 << i  # 记录当前要操作的 bit
            for num in nums:
                if num & bit != 0:
                    cnt += 1
            if cnt % 3 != 0:
                # 不等于0说明唯一出现的数字在这个 bit 上是1
                res |= bit

        return res - 2 ** 32 if res > 2 ** 31 - 1 else res

为什么Python最后需要对返回值进行判断?
如果不这么做的话测试用例是[-2,-2,1,1,-3,1,-3,-3,-4,-2] 的时候,就会输出 4294967292。 其原因在于Python是动态类型语言,在这种情况下其会将符号位置的1看成了值,而不是当作符号“负数”。 这是不对的。 正确答案应该是 - 4,-4的二进制码是 1111…100,就变成 2^32-4=4294967292,解决办法就是 减去 2 ** 32 。

 func singleNumber(_ nums: [Int]) -> Int {
        var  res = 0
        for i in 0..<32 {
           var  bit = 1 << i
            var cnt = 0
            for num in nums {
               if (bit & num) != 0 {
                   cnt += 1
               }
            }
            if cnt % 3 != 0 {
                res |= bit
            }
        }
        return res
    }

137. 只出现一次的数字 II

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
由于异或的特点是相等归0,可以通过这个特点来进行消重,由于有三个相同的数的存在,所以,用两个变量来存储,一个记第一次和第三次出现,一个记第二次出现。

就有了如下过程:
1)某数第一次出现(即b中不存在),存于a
2)某数第二次出现(即a中存在,故从a中清除),存于b
3)某数第三次出现,本来要存于a,结果b中存在,所以,清零

如果某数出现了3次,就被清零,出现了一次,依然存于a中
a最终存储的数据即为出现了一次的那个数。

    func singleNumber(_ nums: [Int]) -> Int {
        var a : Int = 0
        var b : Int = 0
        nums.forEach { (num : Int) in
            a = (num ^ a) & ~b
            b = (num ^ b) & ~a
        }
        return a
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值