547 朋友圈
class union:
def __init__(self,n):
self.pre=[i for i in range(n)]
self.rank=[1 for i in range(n)]
self.cnt=n
def find(self,x):
#注意这里是while,不是if
while x !=self.pre[x]:
x=self.pre[x]
return x
def union(self,p,q):
parent_p=self.find(p)
parent_q=self.find(q)
if parent_p!=parent_q:
if self.rank[parent_p]<self.rank[parent_q]:
self.pre[parent_p]=parent_q
elif self.rank[parent_p]>self.rank[parent_q]:
self.pre[parent_q]=parent_p
else:
self.pre[parent_q]=parent_p
self.rank[parent_p]+=1
self.cnt-=1
class Solution:
def findCircleNum(self, M: List[List[int]]) -> int:
n=len(M)
un=union(n)
for i in range(n):
for j in range(n):
if M[i][j]==1:
un.union(i,j)
return un.cnt
# res=[]
# for i in range(n):
# root=un.find(i)
# if root not in res:
# res.append(root)
# return len(res)
990 等式方程的可满足性
class union:
def __init__(self,n):
self.pre=[i for i in range(n)]
def find(self,x):
while x!=self.pre[x]:
x=self.pre[x]
return x
def union(self,p,q):
if self.connect(p,q):
return
parent_p=self.find(p)
parent_q=self.find(q)
if parent_p!=parent_q:
self.pre[parent_p]=parent_q
def connect(self,p,q):
return self.find(p)==self.find(q)
class Solution:
def equationsPossible(self, equations: List[str]) -> bool:
n=26
un=union(n)
for i in equations:
if i[1]=='=':
idx1=ord(i[0])-ord('a')
idx2=ord(i[3])-ord('a')
un.union(idx1,idx2)
for i in equations:
if i[1]=='!':
idx1=ord(i[0])-ord('a')
idx2=ord(i[3])-ord('a')
#不等式两端属于同一个并查集说明不符合条件
if un.connect(idx1,idx2):
return False
return True
位运算:
136. 只出现一次的数字
class Solution:
def singleNumber(self, nums: List[int]) -> int:
single_num=0
for num in nums:
single_num^=num
return single_num
137 只出现一次的数字二
如果所有数字都出现了 3 次,那么每一列的 1 的个数就一定是 3 的倍数。之所以有的列不是 3 的倍数,就是因为只出现了 1 次的数贡献出了 1。所以所有不是 3 的倍数的列写 1,其他列写 0 ,就找到了这个出现 1 次的数。
那么需要我们将数字转为二进制,统计了每一位的 1 的个数。我们使用了一个 32位 的 int 来统计。事实上,我们只需要看它是不是 3 的倍数,所以我们只需要两个 bit 位就够了。初始化为 00,遇到第一个 1 变为 01,遇到第二个 1 变为 10,遇到第三个 1 变回 00 。接下来就需要考虑怎么做到。
因为一共有三个状态,所以我们需要用两个变量来表示状态。用 once 表示是否在状态 1,用 twice 来表示是否在状态 2 。那么两个变量都为 0 就表示在状态 0 。然后可以得到如下的状态转移表:
作者:godweiyang
链接:https://leetcode-cn.com/problems/single-number-ii/solution/zi-dong-ji-wei-yun-suan-zui-xiang-xi-de-tui-dao-gu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
b:
b=~a&(x^b)
a:
a=~b&(x^a)
由于代码的实现中,每一位的计算并不是并行进行的,所以可以通过画一个行列由a,new_b,x构成的卡诺图来利用先生成的位。
class Solution:
def singleNumber(self, nums: List[int]) -> int:
#位运算
# a,b=0,0
# for num in nums:
# b=~a&(b^num)
# a=~b&(a^num)
# return b
#数学方法
a=list(set(nums))
return (sum(a)*3-sum(nums))//2
260. 只出现一次的数字 III
摘自题解
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
bismark=0
for num in nums:
bismark^=num
diff=bismark&(-bismark)
x=0
for num in nums:
#将数组分为包含两个不同的只出现一次元素的数组,这两个元素肯定有一位不同,当bismark中该位为1时,说明就是该位对于两个元素来说不同的
if num & diff:
x ^=num
return [x,bismark^x]
哈希表:时间、空间复杂度均是O(n)
class Solution:
def singleNumber(self, nums: List[int]) -> List[int]:
hash=defaultdict(int)
for num in nums:
if num not in hash:
hash[num]=1
else:
hash[num]+=1
return [x for x in hash if hash[x]==1]
645 错误的集合
class Solution:
def findErrorNums(self, nums: List[int]) -> List[int]:
err=sum(nums)-sum(set(nums))
res=0
n=len(nums)
for num in nums:
- List item
res^=num
for i in range(1,n+1):
res^=i
return [err,err^res]
399. 除法求值
带权的图
对每个equation如"a/b=v"构造a到b的带权v的有向边和b到a的带权1/v的有向边,
之后对每个query,只需要进行dfs并将路径上的边权重叠乘就是结果了,如果路径不可达则结果为-1。
class Solution:
def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
graph=defaultdict(list)
weight=defaultdict(int)
for idx,equ in enumerate(equations):
graph[equ[0]].append(equ[1])
graph[equ[1]].append(equ[0])
weight[(equ[0],equ[1])]=values[idx]
weight[(equ[1],equ[0])]=float(1/values[idx])
def dfs(start,end,visited):
#这条边已经存在,则直接输出权值
if (start,end) in weight:
return weight[(start,end)]
#图中没有这个点,用0来表示容易跳出
if start not in graph or end not in graph:
return 0
#已经被访问过
if start in visited:
return 0
visited.append(start)
for node in graph[start]:
res=weight[(start,node)]*dfs(node,end,visited)
#找到第一个结果不为0的就弹出
if res!=0:
#将结果加入,以便于以后使用
weight[(start,end)]=res
break
visited.remove(start)
return res
res=[]
for q0,q1 in queries:
ans=dfs(q0,q1,[])
#为0,说明不存在,则输出-1
if ans==0:
ans=-1.0
res.append(ans)
return res
560. 和为K的子数组
sum(i,j)=sum(0,j)-sum(0,i),计算到前i个数字和sum(0,i)时,只需要判断sum(0,i)-k是否存在哈希表中,即是否存在sum(i,j)=k,sum(0,i)=sum(0,i)-k,则说明从i-j的连续子数组和为k
利用 hashmap 记录和的累加值来避免重复计算
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
n=len(nums)
hash=defaultdict(int)
#初始化一个0,用于计算从开始到i的累加和刚好是k
hash[0]=1
pre=0
count=0
for i in range(n):
pre+=nums[i]
if pre-k in hash:
count+=hash[pre-k]
if pre in hash:
hash[pre]+=1
else:
hash[pre]=1
return count
523 连续的子数组和
如果 k = 0, 当且仅当sum[i…j] = 0的时候 i-j之间大于1,return True
如果 k != 0, sum[i…j]是k的倍数的时候,return True
用哈希表记录前缀和对k的余数
class Solution:
def checkSubarraySum(self, nums: List[int], k: int) -> bool:
hash=defaultdict(int)
n=len(nums)
if n<2:
return False
pre=0
hash[0]=-1
for i in range(n):
pre+=nums[i]
if k!=0:
pre=pre%k
if pre in hash:
#不能用=,防止[0,1,0] 0
if i-hash[pre]>1:
return True
else:
hash[pre]=i
return False
974. 和可被 K 整除的子数组
class Solution:
def subarraysDivByK(self, A: List[int], K: int) -> int:
#用哈希表记录前缀和对K的余数 ,若余数存在则说明这之间的连续数组元素和可以被K整除
hash=defaultdict(int)
pre=0
count=0
#有些数本身就能整除K,所以需要初始化余数0为1
hash[0]=1
n=len(A)
for i in range(n):
pre+=A[i]
if K!=0:
pre%=K
if pre in hash:
count+=hash[pre]
hash[pre]+=1
else:
hash[pre]=1
return count
713. 乘积小于K的子数组
如果一个子串的乘积小于k,那么他的每个子集都小于k
计算[10, 5]这个数组的子串是,只加入[5]和[10, 5],而不加入[10],这部分的子串数量刚好是r - l + 1
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
if k<=1:
return 0
left,ans=0,1
count=0
#right指针一直向右遍历,每次循环找到以nums[r]为结尾的乘积小于k的连续数组个数
for r,val in enumerate(nums):
ans*=val
# left向右移动,直到找到乘积小于k的第一个left,此时left-right之间的数组乘积都小于k
while ans>=k:
ans/=nums[left]
left+=1
count+=r-left+1
return count