442. 数组中重复的数据
法一:通过索引号排序,比如数字4放到索引3的位置,最后找排序后数组,与索引号没有相差1便是重复元素,从前到后依次将数据放到合适的索引
nums[nums[i]-1], nums[i] = nums[i], nums[nums[i]-1] 和
nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]不一样
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
n=len(nums)
res=[]
for i in range(n):
while nums[i]!=nums[nums[i]-1]:
nums[nums[i]-1],nums[i]=nums[i],nums[nums[i]-1]
for idx,val in enumerate(nums):
if idx !=val-1:
res.append(val)
return res
例:
nums=[4,3,2,7,8,2,3,1]
排序后的数组
法二:因为有元素出现两次,则找到数字 i 时,将索引为 i-1 处的数字翻转为负数。
如果位置i-1 上的数字已经为负,则i是出现两次的数字。依然是利用索引
class Solution:
def findDuplicates(self, nums: List[int]) -> List[int]:
n=len(nums)
res=[]
for i in range(n):
idx=abs(nums[i])-1
if nums[idx]<0:
res.append(idx+1)
nums[idx]=-nums[idx]
return res
448 找到所有数组中消失的数字
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
n=len(nums)
res=[]
for i in range(n):
while nums[nums[i]-1]!=nums[i]:
nums[nums[i]-1],nums[i]=nums[i],nums[nums[i]-1]
for idx,val in enumerate(nums):
if val-1!=idx:
res.append(idx+1)
return res
遍历到数字i时,如果索引i-1处数字大于0,则将索引i-1处数字翻转,如果已经小于0,说明已经遍历过数字i,
重新遍历数组,数字大于0处的索引值+1即为不存在的数字
class Solution:
def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
n=len(nums)
res=[]
for i in range(n):
idx=abs(nums[i])-1
if nums[idx]>0:
nums[idx]=-nums[idx]
for i in range(n):
if nums[i]>0:
res.append(i+1)
return res
41 缺失的第一个正数
class Solution:
def firstMissingPositive(self, nums: List[int]) -> int:
n=len(nums)
if 1 not in nums:
return 1
if n==1:
return 2
# 负数和超过N的数不需要考虑,因为是最小正整数
for i in range(n):
if nums[i]<=0 or nums[i]>n:
nums[i]=1
for i in range(n):
idx=abs(nums[i])-1
if nums[idx]>0:
nums[idx]=-nums[idx]
for i in range(n):
if nums[i]>0:
return i+1
#遍历完无结果,说明数组长度内的正数数字不缺,返回n+1
return n+1
851 喧闹和富有
class Solution:
def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]:
queue=defaultdict(list)
n=len(quiet)
for i,v in richer:
queue[v].append(i)
answer=[0]*n
def dfs(node):
# 如果已经知道node点的子树和该点中最安静的,则直接返回这个最安静的点
if answer[node]:
return answer[node]
if not answer[node]:
answer[node]=node
for child in queue[node]:
#子树中最安静的
cur=dfs(child)
#子树中最安静的人可以是 person 本身,或者是 person 的孩子结点的某个子树中最安静的人。
if quiet[cur]<quiet[answer[node]]:
answer[node]=cur
#返回钱数大于node点钱数的最安静的那个点
return answer[node]
#需要知道每个点的,拥有钱数多于等于该点的,最安静的点
for i in range(n):
dfs(i)
return answer
507 完美数
import math
class Solution:
def checkPerfectNumber(self, num: int) -> bool:
if num<=0:
return False
sum=0
for i in range(1,int(num**0.5)+1):
if num%i==0:
sum+=i
if i*i !=num:
sum+=num/i
return sum==2*num
728 自除数
结果应该不算空间复杂度
class Solution:
def selfDividingNumbers(self, left: int, right: int) -> List[int]:
def is_divid(n):
for d in str(n):
if d=='0' or n%int(d)>0 :
return False
return True
res=[]
for i in range(left,right+1):
if is_divid(i):
res.append(i)
return res
794. 有效的井字游戏
class Solution:
def validTicTacToe(self, board: List[str]) -> bool:
x_count,o_count=0,0
for bo in board:
x_count+=bo.count('X')
o_count+=bo.count('O')
def winner(board,player):
for i in range(3):
#有一行有三个相同字符
if all(board[i][j]==player for j in range(3)):
return True
#有一列有三个相同字符
if all(board[j][i]==player for j in range(3)):
return True
#对角线存在三个相同字符
if player==board[0][0] and player==board[1][1] and player==board[2][2]:
return True
if player==board[0][2] and player==board[1][1] and player==board[2][0]:
return True
#O的数目和x相等或者差1
if o_count not in {x_count-1,x_count}:
return False
#如果第一个人获胜,则X数目比O多一
if winner(board,'X') and x_count-1!=o_count:
return False
#如果第二个人获胜,则X数目与O相同
if winner(board,'O') and x_count!=o_count:
return False
return True
1286 字母组合迭代器
class CombinationIterator:
def __init__(self, characters: str, combinationLength: int):
self.queue=[]
#获得字符串的排列组合
def combination(num,k):
if k==0:
return ['']
substr=[]
for i in range(len(num)):
for j in combination(num[i+1:],k-1):
substr+=[num[i]+j]
return substr
self.queue=combination(characters,combinationLength)
def next(self) -> str:
if self.queue:
return self.queue.pop(0)
def hasNext(self) -> bool:
return self.queue !=[]
回溯法:
def __init__(self, characters: str, combinationLength: int):
# self.com = itertools.permutations(characters,combinationLength)
# self.stack = []
self.com = collections.deque()
def helper(characters,res=''):
if len(res)==combinationLength:
self.com.append(res)
for i in range(len(characters)):
helper(characters[i+1:],res+characters[i])
helper(characters)
622 设计循环队列
任何数据结构中都不存在环形结构,但是可以使用一维 数组 模拟,通过操作数组的索引构建一个 虚拟 的环。很多复杂数据结构都可以通过数组实现。
class MyCircularQueue:
def __init__(self, k: int):
self.queue=[0]*k
#队首索引
self.headindex=0
#队列长度
self.count=0
self.k=k
def enQueue(self, value: int) -> bool:
if self.count==self.k:
return False
else:
#队尾添加元素
self.queue[(self.headindex+self.count)%self.k]=value
self.count+=1
return True
def deQueue(self) -> bool:
if self.count==0:
return False
else:
#队首索引后移,因为先进先出,所以删除队首元素
self.headindex=(self.headindex+1)%self.k
self.count-=1
return True
def Front(self) -> int:
if self.count==0:
return -1
else:
return self.queue[self.headindex]
def Rear(self) -> int:
if self.count==0:
return -1
else:
#队尾索引(self.headindex+self.count-1)%self.k
return self.queue[(self.headindex+self.count-1)%self.k]
def isEmpty(self) -> bool:
return self.count==0
def isFull(self) -> bool:
return self.count==self.k
684. 冗余连接
并查集实现:
我们可以把相互连通的点看成一个组,如果现在查询的点对分别在不同的组中,则这个点对不连通,否则连通。
三步走:
1.初始化pre和rangks数组,pre为每个元素的父结点(上一级),ranks为每个元素作为根节点时的树的秩(树的深度),一开始设置pre的每个值为每个元素自己,设置ranks每个值为0.
2.find() 查询该元素的首级,顺便做路径压缩
3.join() 合并两个集合,按秩合并,秩小的树的根节点指向秩大的树的根节点。
————————————————
版权声明:本文为优快云博主「AivenZ」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/AivenZhong/article/details/89148862
# 这里记录一下一种新的数据结构:并查集
def find(x, pre):
"""
查找x的最上级(首级)
:param x: 要查找的数
:param pre: 每个元素的首级
:return: 元素的上一级
"""
root, p = x, x # root:根节点, p:指针
# 找根节点
while root != pres[root]:
root = pres[root]
# 路径压缩,把每个经过的结点的上一级设为root(直接设为首级)
while p != pres[p]:
p, pres[p] = pres[p], root
return root
def join(x, y, pre, ranks):
"""
合并两个元素(合并两个集合)
:param x: 第一个元素
:param y: 第二个元素
:param pre: 每个元素的上一级
:param ranks: 每个元素作为根节点时的秩(树的深度)
:return: None
"""
h1, h2 = find(x, pre), find(y, pre)
# 当两个元素不是同一组的时候才合并
# 按秩合并
if h1 != h2:
if ranks[h1] < ranks[h2]:
pre[h1] = h2
else:
pre[h2] = h1
if ranks[h1] == ranks[h2]:
ranks[h1] += 1
# 结点数
n = 10
# 边数据
data = [[0, 9], [9, 3], [1, 2], [2, 8], [4, 5], [6, 7], [0, 5], [6, 8]]
# pre一开始设置每个元素的上一级是自己,ranks一开始设置每个元素的秩为0
pre, ranks = [i for i in range(n)], [0] * n
for edge in data:
join(edge[0], edge[1], pre, ranks)
print('pre:\t', pre)
print('ranks:\t', ranks)
print('idx:\t', list(range(n)))
# pre: [0, 6, 1, 0, 0, 4, 6, 6, 1, 0]
# ranks: [2, 1, 0, 0, 1, 0, 2, 0, 0, 0]
# idx: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
并查集的初始化就是每个点只属于自己标记的集合,即p[x]=x。作为索引的x是指当前节点,作为值的x是指当前节点所在集合的父节点(即地址指针)
遍历边,如果两个点集合不同,那就合并进同一个集合
如果遍历边的过程中,发现两个点已经加进过之前的集合了,那就说明成环了,这时就可以输出了。
class Solution:
def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
n=len(edges)
p=[[i] for i in range(n+1)]#并查集初始化,每个点只属于自己标记的集合
for x,y in edges:
if p[x]!=p[y]:#如果两个集合地址不一致,根据权重合并
if len(p[x])<len(p[y]):
x,y=y,x
p[x].extend(p[y])
for z in p[y]:#修改合并进去元素的地址指针
p[z]=p[x]
else:
return [x,y] #存在重复,说明之前已经连通,说明成环,这条边多余可以删除
721 账户合并:
每个账户由名称和邮箱地址组成,不同账户中,只要有相同的一个邮箱地址,则合并为一个账户
所以可以把账户看作一个点,只要两个账户有相同的邮箱地址,则将两个点连起来
class union:
def __init__(self,n):
#记录上级
self.pre=[i for i in range(n)]
#记录权重
self.rank=[1 for _ in range(n)]
#查找上级
def find(self,x):
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
class Solution:
def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
# 记录已经出现过的邮箱地址:账户
lookup = {}
n = len(accounts)
un = union(n)
# 第一步,找到相关联的账户,并使用并查集记录
# 有一个相同的邮箱,则可以合并为同一个账户
# 用lookup记录已经出现过的邮箱对应的账户(索引),以便于邮箱再次出现时合并两个账户
for idx,account in enumerate(accounts):
for email in account[1:]:
if email in lookup:
un.union(idx,lookup[email])
else:
#如果邮箱未出现过,则账户就是他的索引
lookup[email]=idx
# 将相关联账户的邮箱地址合并
join_count=defaultdict(set)
for i in range(n):
root=un.find(i)
for em in accounts[i][1:]:
#将属于同一个账户的所有邮箱合并起来
join_count[root].add(em)
res=[]
#输出结果
for key,value in join_count.items():
res.append([accounts[key][0]]+list(sorted(value)))
return res