这种题基本就是碰一次死一次…根本不知道什么时候会用位运算。
题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)