位运算(更新至8.31)

这篇博客探讨了如何利用位运算解决数组中出现一次的数字问题,以及如何统计特殊整数的数量。通过位运算技巧,文章详细解释了如何在O(n)时间和O(1)空间复杂度下找到数组中仅出现一次的两个数字,并提供了相应的Python代码实现。同时,介绍了数位DP方法来计算区间内特殊整数的个数,给出了具体算法流程和关键步骤。
摘要由CSDN通过智能技术生成

这种题基本就是碰一次死一次…根本不知道什么时候会用位运算。

题1

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

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

这题包含挺多知识点的,结果我一个都不会。

知识点1:用异或找出一个不同的数字
因为两个相同的数字用异或的结果一定是0,所以数组里只有一个孤零零的数字,所有数字异或的结果就是那个数字。

res = 0
for num in nums:
	res = res ^ num
return res

但本题问的是两个,所以本体最后得出的结果是那两个不同数的异或结果。
res = x ^ y
那怎么区分这两个数呢?

知识点2:从右往左找到res的第一个1---------->这就是x和y第一个不一样的位置--------->通过这个位置能够区分出x和y---------->通过这个位置能够区分出别的数------>通过这个位置能够区分出【x、相同的数】与【y、其他相同的数】

如,假设数组为【3,3,1,1,7,5】,异或的结果为 5(101)^7(111),找出第一个不同的位置是第二位,通过比较第二位将这个数组分成【5,1,1】和【7,3,3】

这个思路真的挺厉害的,区分x和y的同时,也能保证数组中的其他数也被区分且不产生混乱。附上代码:

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        x, y, n, m = 0, 0, 0, 1
        for num in nums:         # 1. 遍历异或
            n ^= num
        while n & m == 0:        # 2. 循环左移,计算 m
            m <<= 1       
        for num in nums:         # 3. 遍历 nums 分组
            if num & m: x ^= num # 4. 当 num & m != 0
            else: y ^= num       # 4. 当 num & m == 0
        return x, y              # 5. 返回出现一次的数字

重点说一下m。m初始为1,左移x次变为10…0(x - 1个0),之后根据数的第x位进行区分使用的是 num & m:如果num的第x位为1,结果不为0;反之,结果为0.

数位DP

数位dp所解决的问题主要是在数[x, y]中(x通常是0),满足条件的数有几个。

统计特殊整数
如果一个正整数每一个数位都是 互不相同 的,我们称它是 特殊整数 。

给你一个 正 整数 n ,请你返回区间 [1, n] 之间特殊整数的数目。
示例 1:
输入:n = 20
输出:19
解释:1 到 20 之间所有整数除了 11 以外都是特殊整数。所以总共有 19 个特殊整数。

i:第几位
mask:之前记录了哪些数字,集合,集合甚至可以用int表示
is_limit:当前填的数字是否受到约束(比如,123)如果高位填1,第二位就会受到限制。
is_numr:前面是否填过数字。举例:010合法,但会被误认为不合法。如果填过数字,可以从0开始遍历,如果没填过数字,我们可以选择①跳过②从1开始填数字

def countSpecialNumbers(self, n: int) -> int:
	s = str(n)
	#返回从i开始填数字,i前面的集合是mask,能构造出特殊整数的个数。
	@cache
	def f (i: int, mask: int, is_limit: bool, is_num: bool) -> int:
		if i == len(s):
			return int(is_num) #有一种特殊情况是000000,得排除
		res = 0
		if not is_num: #选择跳过,不填数字
			res = f(i + 1, mask, False, False)
		up = int(s[i]) if is_limit else 9
		down = 0 if is_num else 1
		#枚举要填的数
		for d in range(down, up + 1):
			if mask >> d & 1 == 0: #mask里没有d
			# 注意is_limit的判定条件。
				res += f(i + 1, mask|(1<<d),is_limit and d == up, True)
		return res
	return f(0, 0, True, False)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值