对于每一轮,我们考虑的是与上一轮格子相邻的,未被计算过的格子x,其高度必然比上一轮的格子高度多1。
可以发现,上述过程就是从水域出发,指向广度优先搜索的过程。因此,记录下(多个)所有水域的位置,然后执行广度优先搜索,计算出所有陆地格子的高度,即为答案。这就是多源BFS。可用原矩阵进行访问过。
class Solution(object):
def highestPeak(self, isWater):
"""
:type isWater: List[List[int]]
:rtype: List[List[int]]
"""
queue, m, n, cost = [], len(isWater), len(isWater[0]), 0
for i, row in enumerate(isWater):
for j, val in enumerate(row):
# 水域,作为起点入队,并更新为答案需要返回的0
if val:
isWater[i][j] = 0
queue.append((i, j))
# 陆地:先更新为无限大的高度,等BFS时更新它
else:
isWater[i][j] = float("inf")
while queue:
nxt = []
cost += 1
for i, j in queue:
for dx, dy in (0, 1), (1, 0), (-1, 0), (0, -1):
# 只有没被更新过的陆地才能被更新,否则已经有更近的水域访问过它了
nx=i+dx
ny=j+dy
if 0 <= nx < m and 0 <= ny < n and isWater[nx][ny] > cost:
#在python3中可以直接用海象运算符写为0<=(nx:=i+dx)cost
isWater[nx][ny] = cost
nxt.append((nx, ny))
queue = nxt
return isWater
其实等价于找出:
在两个句子中一共只出现一次的单词。
因此我们可以使用一个哈希映射统计两个句子中单词出现的次数。哈希映射中的每个键值对,键表示一个单词,值表示该单词出现的次数。在统计完成后,我们再对哈希映射进行一次遍历,把所有值为1的键放入答案中即可。
class Solution(object):
def uncommonFromSentences(self, s1, s2):
"""
:type s1: str
:type s2: str
:rtype: List[str]
"""
freq=Counter(s1.split( ))+Counter(s2.split( ))#用法非常妙!
ans=list()
for word,count in freq.items():
if count==1:
ans.append(word)
return ans
class Solution(object):
def numberOfSteps(self, num):
"""
:type num: int
:rtype: int
"""
count=0
while num!=0:
if num%2==0:#偶数
num=num//2
else:#奇数
num-=1
count+=1
return count
算法思想:
用比较暴力的方法:枚举遍历子串temp,统计该子串的字典键值对,添加到集合中,如果这是小/大写字母,判断字典中有没有大/小写,没有就false, 找到了就true,返回temp。
from collections import Counter
class Solution(object):
def longestNiceSubstring(self, s):
"""
:type s: str
:rtype: str
"""
for i in range(len(s)-1,0,-1):#i字符串长度,从大到小
for j in range(len(s)-i):#长度为i时,开始的位置有0到len()-i-1
temp=s[j:j+i+1]#截取这个子串
temp_dict=Counter(temp)#字典统计出现这个子串字符次数
temp_set=set()
flage=True
for key in temp_dict:
if key not in temp_set:
temp_set.add(key.lower())
if key.islower():
key_=key.upper()
else:
key_=key.lower()
if key_ not in temp_dict:
flage=False
break
if flage:
return temp
return ""
算法思路:首先查找ch在字符串word的位置,如果找到,则将字符串从下标0开始,到查找到的ch所在位置为止的那段字符串进行反转,否则直接返回原字符串。
注:找字符ch在字符串word里出现的下标位置,这题我们应该用find()
class Solution(object):
def reversePrefix(self, word, ch):
"""
:type word: str
:type ch: str
:rtype: str
"""
i=word.find(ch)+1
return word[:i][::-1]+word[i:]
算法思路:递归
我们最终选取的数列中的数必然是没有相邻的,因为相邻的话我们可以直接取下一个斐波那契数(他们的和),毕竟取一个比取两个肯定取了更少的数。最接近k的斐波那契数必须被选。取完后后面依然是一个递归求解。
大白话:1,1,2,3,5,8,13这个数列中,你会发现13比8+3+1大1。8等于5+2+1。
class Solution(object):
def findMinFibonacciNumbers(self, k):
"""
:type k: int
:rtype: int
"""
if k==0:
return 0
f,f1=1,1
while f1<=k:
f,f1=f1,f+f1
return 1+self.findMinFibonacciNumbers(k-f)
算法思路:一次遍历
记l和w为某个矩形的长度和宽度,设k为可以从这个矩形中切出的最大正方形的边长,则有k=min(l,w)。我们遍历输入数组,维护两个变量,maxLen表示遍历到当前矩形时的所有可以切出的最大正方形的边长的最大值,res表示可以切出边长为maxLen的正方形的个数。计算当前的k,当k=maxLen时,对res进行加1操作;当k>maxLen时,则更新maxLen为k,并把res重置为1。
class Solution(object):
def countGoodRectangles(self, rectangles):
"""
:type rectangles: List[List[int]]
:rtype: int
"""
res,maxLen=0,0
for l,w in rectangles:
k=min(l,w)
if k==maxLen:
res+=1
elif k>maxLen:
res=1
maxLen=k
return res
算法思路:回溯算法
首先在mXn个网格内枚举起点。只要格子内的数大于0,它就可以作为起点进行开采。记枚举的起点为(i,j),我们就可以从(i,j)开始进行递归+回溯,枚举所有可行的开采路径。我们用递归函数dfs(x,y,gold)进行枚举,其中(x,y)表示所在的位置,gold表示开采位置(x,y)之前,已拥有的黄金数量。根据题目的要求,我们需要进行如下步骤:
需要注意的是,题目规定了“每个单元格只能被开采一次”,因此当前我们到达位置(x,y)时,我们可以将grid[x][y]暂时置为0;在进行回溯之前,再将grid[x][y]的值恢复。
class Solution(object):
def getMaximumGold(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m=len(grid)
n=len(grid[0])
global ans
ans=0
def dfs(x,y,gold):
gold=gold+grid[x][y]
global ans
ans=max(ans,gold)
res=grid[x][y]
grid[x][y]=0#遍历过,标记
for nx,ny in ((x-1,y),(x+1,y),(x,y-1),(x,y+1)):
if 0<=nx<m and 0<=ny<n and grid[nx][ny]>0:#符合条件遍历
dfs(nx,ny,gold)
grid[x][y]=res#遍历完恢复原来值
for i in range(m):
for j in range(n):
if grid[i][j]!=0:#依次遍历
dfs(i,j,0)
return ans
算法思路:用哈希表,字典记录元素出现次数,次数为1即为唯一元素,累加。
from collections import defaultdict
dict1 = defaultdict(int)
dict2 = defaultdict(set)
dict3 = defaultdict(str)
dict4 = defaultdict(list)
#输出默认值
print(dict1[1])# 0
print(dict2[1])# set()
print(dict3[1])#
print(dict4[1])# []
class Solution(object):
def sumOfUnique(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
dic=defaultdict(int)
res=0
for i in nums:
dic[i]+=1
for m,n in dic.items():
if n==1:
res+=m
return res
class Solution(object):
def longestDiverseString(self, a, b, c):
"""
:type a: int
:type b: int
:type c: int
:rtype: str
"""
ans=[]
cnt=[[a,'a'],[b,'b'],[c,'c']]
while True:
cnt.sort(key=lambda x:-x[0])#从大到小排序,先排数量最多的
isNext=False
for i,(c,ch) in enumerate(cnt):
if c<=0:#没有该字母
break
if len(ans)>=2 and ans[-2]==ch and ans[-1]==ch:#会成为三个连续字母,跳过
continue
isNext=True
ans.append(ch)
cnt[i][0]-=1
break
if not isNext:#所有都遍历过了,才输出
return "".join(ans)
解题思路:哈希表,数组
维护四个计数和点的集合。
四个计数分别为:行计数、列计数、左对角线计数、右对角线计数。这样我们只需要知道查询点在任意计数上是否大于0,就知道它是不是被照亮了。再根据点的集合删除点,删除点的时候同样更新各个计数情况即可。
class Solution(object):
def gridIllumination(self, n, lamps, queries):
"""
:type n: int
:type lamps: List[List[int]]
:type queries: List[List[int]]
:rtype: List[int]
"""
row_cnts,col_cnts,lr_cnts,rl_cnts,points=defaultdict(int),defaultdict(int),defaultdict(int),defaultdict(int),set()
#行计数,列计数,左对角计数,右对角计数(用字典,键值对次数),亮灯计数(用集合)
for r,c in lamps:#遍历开灯的地方
if (r,c) not in points:#统计各方向开灯数,不是灯亮数!
points.add((r,c))#(r,c)=(x,y)
row_cnts[r]+=1
col_cnts[c]+=1
#r*(-1)+b=c
lr_cnts[r+c]+=1
#r+b=c
rl_cnts[r-c]+=1
ans=[0]*len(queries)
for i in range(len(queries)):
r,c=queries[i]#灭灯坐标
if row_cnts[r] or col_cnts[c] or lr_cnts[r+c] or rl_cnts[r-c]:#只有有一个方向开灯,就说明这是亮的
ans[i]=1
for dx,dy in (0,1),(1,0),(0,-1),(-1,0),(0,0),(1,1),(-1,1),(1,-1),(-1,-1):#去判断是哪个方向上的灯,并关掉
nx=r+dx
ny=c+dy
if (nx,ny) in points:#是开灯,各方向计数-1
points.remove((nx,ny))
row_cnts[nx]-=1
col_cnts[ny]-=1
lr_cnts[nx+ny]-=1
rl_cnts[nx-ny]-=1
return ans
算法思路:哈希表+一次遍历
我们进行一次遍历,遍历时下标代表j。对每一个j,我们需要知道在这个j之前的符合条件的i的个数,即满足|nums[i]-nums[j]|=k的i的个数,亦即满足nums[i]=nums[j]+k或nums[j]-k的i的个数。使用哈希表可以在O(1)的时间内统计出这样的个数,因此在遍历时我们可以使用一个哈希表来维护不同数值的频率,并统计符合条件的数对数。
class Solution(object):
def countKDifference(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
res=0
cnt=defaultdict(int)#哈希字典用于统计
for num in nums:
res+=cnt[num+k]+cnt[num-k]#和前面的匹配
cnt[num]+=1#统计已出现次数
return res
解题思路:暴力模拟
由于要保证分数在(0,1)范围内,我们可以枚举分母denominator∈[2,n]和分子numerator∈[1,denominator),若分子分母的最大公约数为1,则我们找到了一个最简分数。
class Solution(object):
def gcd(x,y):
while x%y!=0:
x,y=y,x%y
return y
def simplifiedFractions(self, n):
"""
:type n: int
:rtype: List[str]
"""
def gcd(x,y):
while x%y!=0:
x,y=y,x%y
return y
ls=[]
for i in range(2,n+1):
for j in range(1,i):#分子j是小于分母i的
if gcd(i,j)==1:
f=str(j)+"/"+str(i)
ls.append(f)
return ls
算法思路:排序
首先对数组nums进行升序排序,随后使用一个大小固定为k的滑动窗口在nums上进行遍历。记滑动窗口的左边界为i,那么右边界即为i+k-1,窗口中的k名学生最高分和最低分的差值即为nums[i+k-1]-nums[i]。最终的答案即为所有nums[i+k-1]-nums[i]中的最小值。
class Solution(object):
def minimumDifference(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
nums.sort()
minnum=("float")
for i in range(len(nums)-k+1):
a=nums[i+k-1]-nums[i]
if minnum>a:
minnum=a
return minnum
解题思路:多源BFS
从四条外边的陆地出发的BFS,先把边界的陆地加入要遍历点的队列中,把1变为0。四个方向遍历,把能走到的陆地1变为0,再加入要遍历点的队列中。最后计算不能到达的陆地格子数量(地图中还是1的数量/值总和)。
class Solution(object):
def numEnclaves(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
m=len(grid)
n=len(grid[0])
queue=deque()#存放满足条件的,作为bfs源点
for i in range(m):
if grid[i][0]:#第一列
queue.append((i,0))
grid[i][0]=0
if grid[i][n-1]:#最后一列
queue.append((i,n-1))
grid[i][n-1]=0
for j in range(n):
if grid[0][j]:#第一行
queue.append((0,j))
grid[0][j]=0
if grid[m-1][j]:#最后一行
queue.append((m-1,j))
grid[m-1][j]=0
while queue:
x,y=queue.popleft()
for dx,dy in ((0,-1),(-1,0),(0,1),(1,0)):
nx=x+dx
ny=y+dy
if 0<=nx<m and 0<=ny<n and grid[nx][ny]:#满足条件,要继续遍历
queue.append((nx,ny))
grid[nx][ny]=0
return sum(sum(g) for g in grid)
算法思路:统计
统计字符串中字母‘a’,‘b’,‘l’,‘o’,‘n’ 的数量即可。其中每个字母 “balloon” 需要两个‘l’,‘o’,可以将字母‘l’,‘o’ 的数量除以 22,返回字母 ‘a’,‘b’,‘l’,‘o’,‘n’ 中数量最小值即为可以构成的单词数量。
class Solution(object):
def maxNumberOfBalloons(self, text):
"""
:type text: str
:rtype: int
"""
cnt=Counter(ch for ch in text if ch in 'balon')
cnt['l']=cnt['l']//2
cnt['o']=cnt['o']//2
# print(cnt)
return min(cnt.values()) if len(cnt)==5 else 0 #若不这样写,字母不足时,会返回已有的最小数值不为0,错误
算法思路:偶数下标的二分查找
考虑没有单个元素的情况:偶数下标的值与右边的值相等
考虑有一个单个元素的情况:则该元素一定在偶数下标,且他不与右边的值相等。
在偶数下标范围内,进行二分查找。找到满足nums[x]/nums[x+1]的最小的偶数下标x,则下标x处的元素是只出现一次的元素。
注意
class Solution(object):
def singleNonDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
low=0
high=len(nums)-1
while low<high:
mid=(high+low)//2
mid-=mid&1
if nums[mid]==nums[mid+1]:
low=mid+2
else:
high=mid
return nums[low]
算法思路:数组、矩阵、数学规律
这样的点至多有一个,我们只需要确定某行最小值的最大值与某列最大值的最小值一致,若存在即找到这样的两个数,才存在答案。
自己写的,有点冗长
class Solution(object):
def luckyNumbers (self, matrix):
"""
:type matrix: List[List[int]]
:rtype: List[int]
"""
m=len(matrix)
n=len(matrix[0])
max_x=-1
min_y=("float")
# max_ls=[]#存每列的最大值
min_ls=[]#存每行的最小值
for i in range(m):
for j in range(n):
min_y=min(min_y,matrix[i][j])
min_ls.append(min_y)
min_y=("float")
for j in range(n):
for i in range(m):
max_x=max(max_x,matrix[i][j])
if max_x==max(min_ls):
return [max_x]
max_x=-1
return []
class Solution:
def checkWays(self, pairs: List[List[int]]) -> int:
adj = defaultdict(set)
for x, y in pairs:
adj[x].add(y)
adj[y].add(x)
# 检测是否存在根节点
root = next((node for node, neighbours in adj.items() if len(neighbours) == len(adj) - 1), -1)
if root == -1:
return 0
ans = 1
for node, neighbours in adj.items():
if node == root:
continue
currDegree = len(neighbours)
parent = -1
parentDegree = maxsize
# 根据 degree 的大小找到 node 的父节点 parent
for neighbour in neighbours:
if currDegree <= len(adj[neighbour]) < parentDegree:
parent = neighbour
parentDegree = len(adj[neighbour])
# 检测 neighbours 是否为 adj[parent] 的子集
if parent == -1 or any(neighbour != parent and neighbour not in adj[parent] for neighbour in neighbours):
return 0
if parentDegree == currDegree:
ans = 2
return ans
class Solution:
def knightProbability(self, n: int, k: int, row: int, column: int) -> float:
@functools.lru_cache(None)#缓存,防止超时
def dfs(r,c,counts):
if r<0 or r>n-1 or c<0 or c>n-1:
return 0
if counts==k:
return 1
step=[(-2,1),(-1,2),(1,2),(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1)]
res=0
for i,j in step:
res+=dfs(r+i,c+j,counts+1)
return res/8
return dfs(row,column,0)
解题思路:超级简单的简单题
实际上只需要比较前两组,相同元素便为中心节点
class Solution(object):
def findCenter(self, edges):
"""
:type edges: List[List[int]]
:rtype: int
"""
a,b=edges[0]
i,j=edges[1]
if a==i or a==j:
return a
else:
return b