2021年1月刷题日志
2.4
美赛前的最后一天寒假了
题目
643. 子数组最大平均数 I
难度简单
给定
n
个整数,找出平均数最大且长度为k
的连续子数组,并输出该最大平均数。示例:
输入:[1,12,-5,-6,50,3], k = 4 输出:12.75 解释:最大平均数 (12-5-6+50)/4 = 51/4 = 12.75
提示:
- 1 <=
k
<=n
<= 30,000。- 所给数据范围 [-10,000,10,000]。
思路
没啥花里胡哨的,用滑动窗口模拟就行
代码
class Solution:
def findMaxAverage(self, nums: List[int], k: int) -> float:
cur_sum = 0
for i in range(k-1):
cur_sum += nums[i]
cur_max = -float('inf')
for i in range(k-1,len(nums)):
cur_sum += nums[i]
cur_max = max(cur_max, cur_sum)
cur_sum -= nums[i-k+1]
return cur_max/k
2.3
题目
480. 滑动窗口中位数
难度困难
中位数是有序序列最中间的那个数。如果序列的大小是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4]
,中位数是3
[2,3]
,中位数是(2 + 3) / 2 = 2.5
给你一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
示例:
给出 nums =
[1,3,-1,-3,5,3,6,7]
,以及 k = 3。窗口位置 中位数 --------------- ----- [1 3 -1] -3 5 3 6 7 1 1 [3 -1 -3] 5 3 6 7 -1 1 3 [-1 -3 5] 3 6 7 -1 1 3 -1 [-3 5 3] 6 7 3 1 3 -1 -3 [5 3 6] 7 5 1 3 -1 -3 5 [3 6 7] 6
因此,返回该滑动窗口的中位数数组
[1,-1,-1,3,5,6]
。提示:
- 你可以假设
k
始终有效,即:k
始终小于输入的非空数组的元素个数。- 与真实值误差在
10 ^ -5
以内的答案将被视作正确答案。
思路
暴力法
代码
class Solution:
def medianSlidingWindow(self, nums: List[int], k: int) -> List[float]:
n = len(nums)
res = []
for i in range(n-k+1):
tmp = nums[i:i+k]
tmp.sort()
if len(tmp)%2 == 1:
res.append(tmp[k//2])
else:
res.append((tmp[k//2]+tmp[k//2-1])/2)
return res
2.2
题目
424. 替换后的最长重复字符
难度中等
给你一个仅由大写英文字母组成的字符串,你可以将任意位置上的字符替换成另外的字符,总共可最多替换 k 次。在执行上述操作后,找到包含重复字母的最长子串的长度。
**注意:**字符串长度 和 k 不会超过 104。
示例 1:
输入:s = "ABAB", k = 2 输出:4 解释:用两个'A'替换为两个'B',反之亦然。
示例 2:
输入:s = "AABABBA", k = 1 输出:4 解释: 将中间的一个'A'替换为'B',字符串变为 "AABBBBA"。 子串 "BBBB" 有最长重复字母, 答案为 4。
思路
双指针,滑动窗口,遍历字符串,用一个数组来维护窗口中字母的出现次数
代码
class Solution:
def characterReplacement(self, s: str, k: int) -> int:
cnts = [0]*26
left = right = maxn = 0
n = len(s)
while right < n:
cnts[ord(s[right]) - ord('A')] += 1
maxn = max(maxn, cnts[ord(s[right]) - ord('A')])
if right - left + 1 - maxn > k:
cnts[ord(s[left]) - ord('A')] -= 1
left += 1
right += 1
return right - left
2.1
今日简单题,刷完吃寿喜烧去咯
题目
888. 公平的糖果棒交换
难度简单99
爱丽丝和鲍勃有不同大小的糖果棒:
A[i]
是爱丽丝拥有的第i
根糖果棒的大小,B[j]
是鲍勃拥有的第j
根糖果棒的大小。因为他们是朋友,所以他们想交换一根糖果棒,这样交换后,他们都有相同的糖果总量。(一个人拥有的糖果总量是他们拥有的糖果棒大小的总和。)
返回一个整数数组
ans
,其中ans[0]
是爱丽丝必须交换的糖果棒的大小,ans[1]
是 Bob 必须交换的糖果棒的大小。如果有多个答案,你可以返回其中任何一个。保证答案存在。
示例 1:
输入:A = [1,1], B = [2,2] 输出:[1,2]
示例 2:
输入:A = [1,2], B = [2,3] 输出:[1,2]
示例 3:
输入:A = [2], B = [1,3] 输出:[2,3]
示例 4:
输入:A = [1,2,5], B = [2,4] 输出:[5,4]
提示:
1 <= A.length <= 10000
1 <= B.length <= 10000
1 <= A[i] <= 100000
1 <= B[i] <= 100000
- 保证爱丽丝与鲍勃的糖果总量不同。
- 答案肯定存在。
思路
traversing and binary search.
代码
class Solution:
def fairCandySwap(self, A: List[int], B: List[int]) -> List[int]:
sum_a = sum(A)
sum_b = sum(B)
diff = sum_a-sum_b
for i in A:
j = int(i-diff/2)
if j in B:
return [i,j]
1.31
可恶啊,昨天看到是困难题,甚至CV都给忘了
今天又是并查集
题目
839. 相似字符串组
难度困难
如果交换字符串
X
中的两个不同位置的字母,使得它和字符串Y
相等,那么称X
和Y
两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。例如,
"tars"
和"rats"
是相似的 (交换0
与2
的位置);"rats"
和"arts"
也是相似的,但是"star"
不与"tars"
,"rats"
,或"arts"
相似。总之,它们通过相似性形成了两个关联组:
{"tars", "rats", "arts"}
和{"star"}
。注意,"tars"
和"arts"
是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。给你一个字符串列表
strs
。列表中的每个字符串都是strs
中其它所有字符串的一个字母异位词。请问strs
中有多少个相似字符串组?示例 1:
输入:strs = ["tars","rats","arts","star"] 输出:2
示例 2:
输入:strs = ["omv","ovm"] 输出:1
提示:
1 <= strs.length <= 100
1 <= strs[i].length <= 1000
sum(strs[i].length) <= 2 * 104
strs[i]
只包含小写字母。strs
中的所有单词都具有相同的长度,且是彼此的字母异位词。
思路
题目难度虽然是困难,但是其实具体的实现还是很简单的。
参考了官方题解之后豁然开朗,这道题目其实还是一个传统的并查集题目。
首先看到了我们的最终目标是求相似字符串组,其实就是抽象成并查集里的连通情况。
两个字符串相似,就说明是两个字符串相连(union)。
所以我们只需要维护一个并查集,遍历不同字符串组合确认是否连通,最后输出连通分量的个数即可。
维护并查集很简单,就是纯粹的最简单的模板。
确认字符串的相似性也不难,实现方法应该有很多,我这边就比较随意地写了一个。
- 我们手上有两个字符串
str1
,str2
- 题目的条件是已知这两个字符串长度相等,因此我们直接按照位置来遍历字符串即可
- 新建一个数组
diff_chars
,用来存放不同这两个字符串中的同一个位置上的不同的字符对(x_i, y_i)
,表示第i
个位置上的两个不同字符。 - 同时遍历完两个字符串之后,我们去判断
diff_chars
的长度,只有当长度为的2 的时候,我们才能确认两个字符串有可能相似(在一开始的时候我们就提前判断字符串是否相等) - 相似还需要确认字符对调之后是相同,因此对比一下数组中的两个字符对即可
代码
class UnionFind:
def __init__(self, n):
self.parents = list(range(n))
self.cnt = n
def find(self, x):
if self.parents[x] != x:
self.parents[x] = self.find(self.parents[x])
return self.parents[x]
def union(self, x, y):
f_x, f_y = self.find(x), self.find(y)
if f_x != f_y:
self.parents[f_x] = f_y
self.cnt -= 1
class Solution:
def numSimilarGroups(self, strs: List[str]) -> int:
n = len(strs)
uf = UnionFind(n)
def is_similar(str1, str2):
# 对比两个字符串是否相似
if str1 == str2:
return True
diff_chars = []
for i in range(len(str1)):
if str1[i] != str2[i]:
diff_chars.append((str1[i], str2[i]))
if len(diff_chars)!=2:
return False
a, b = diff_chars[0]
c, d = diff_chars[1]
if a==d and b==c:
return True
else:
return False
for i in range(0, n-1):
for j in range(i+1, n):
# 两两对比 确认相似
str1, str2 = strs[i], strs[j]
if uf.find(i) != uf.find(j):
if is_similar(str1, str2):
uf.union(i,j)
return uf.cnt
1.29
题目
1631. 最小体力消耗路径
难度中等
你准备参加一场远足活动。给你一个二维
rows x columns
的地图heights
,其中heights[row][col]
表示格子(row, col)
的高度。一开始你在最左上角的格子(0, 0)
,且你希望去最右下角的格子(rows-1, columns-1)
(注意下标从 0 开始编号)。你每次可以往 上,下,左,右 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值 的 最大值 决定的。
请你返回从左上角走到右下角的最小 体力消耗值 。
示例 1:
输入:heights = [[1,2,2],[3,8,2],[5,3,5]] 输出:2 解释:路径 [1,3,5,3,5] 连续格子的差值绝对值最大为 2 。 这条路径比路径 [1,2,2,2,5] 更优,因为另一条路径差值最大值为 3 。
示例 2:
输入:heights = [[1,2,3],[3,8,4],[5,3,5]] 输出:1 解释:路径 [1,2,3,4,5] 的相邻格子差值绝对值最大为 1 ,比路径 [1,3,5,3,5] 更优。
示例 3:
输入:heights = [[1,2,1,1,1],[1,2,1,2,1],[1,2,1,2,1],[1,2,1,2,1],[1,1,1,2,1]] 输出:0 解释:上图所示路径不需要消耗任何体力。
提示:
rows == heights.length
columns == heights[i].length
1 <= rows, columns <= 100
1 <= heights[i][j] <= 106
思路
需要抽象成图,每个格子代表一个结点,格子与格子之间的差值的绝对值就是边的权值。
题解中提供了三种思路
- 二分法
- 并查集
- Dijkstra 最短路径
学习了一下 Dijkstra 最短路径算法,简单来说就是启发式搜索,一个利用启发函数的 BFS
维护一个小顶堆 q
,维护一个遍历过的结点集合 visited
,维护一个最短路径长度表 dist
。
-
每次遍历的时候找到当前小顶堆中代价最小的,即所需消耗体力最少的。
-
向四周遍历,假设从当前结点出发到达下一个结点的代价比
dist
的中的值小,则替换,并加入小顶堆,以便下一次可以遍历 -
遍历的时候需要确保当前结点未被遍历过
-
直到走到终点,即可退出遍历
代码
class Solution:
def minimumEffortPath(self, heights: List[List[int]]) -> int:
# dijkstra
m, n = len(heights), len(heights[0])
dist = [0]+[float('inf')]*(m*n-1)
visited = set()
q = [(0,0,0)]
while q:
d, x, y = heapq.heappop(q)
node = x*n + y
if node in visited:
continue
if (x,y) == (m-1,n-1):
break
visited.add(node)
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 max(d, abs(heights[x][y]-heights[nx][ny]))<=dist[nx*n+ny]:
dist[nx*n+ny] = max(d, abs(heights[x][y]-heights[nx][ny]))
heapq.heappush(q, (dist[nx * n + ny], nx, ny))
return dist[m*n - 1]
1.28
昨日困难题又CV了,惭愧
题目
724. 寻找数组的中心索引
难度简单
给定一个整数类型的数组
nums
,请编写一个能够返回数组 “中心索引” 的方法。我们是这样定义数组 中心索引 的:数组中心索引的左侧所有元素相加的和等于右侧所有元素相加的和。
如果数组不存在中心索引,那么我们应该返回 -1。如果数组有多个中心索引,那么我们应该返回最靠近左边的那一个。
示例 1:
输入: nums = [1, 7, 3, 6, 5, 6] 输出:3 解释: 索引 3 (nums[3] = 6) 的左侧数之和 (1 + 7 + 3 = 11),与右侧数之和 (5 + 6 = 11) 相等。 同时, 3 也是第一个符合要求的中心索引。
示例 2:
输入: nums = [1, 2, 3] 输出:-1 解释: 数组中不存在满足此条件的中心索引。
说明:
nums
的长度范围为[0, 10000]
。- 任何一个
nums[i]
将会是一个范围在[-1000, 1000]
的整数。
思路
维护一个当前求得的和,遍历数组找中心索引
代码
class Solution:
def pivotIndex(self, nums: List[int]) -> int:
cur_sum = 0
if len(nums)==1: return 0
tot = sum(nums)
for i,num in enumerate(nums):
target = tot-cur_sum-nums[i]
if cur_sum == target:
return i
cur_sum += num
return -1
1.26
简单题,好耶(然而我却WA了)
题目
1128. 等价多米诺骨牌对的数量
难度简单
给你一个由一些多米诺骨牌组成的列表
dominoes
。如果其中某一张多米诺骨牌可以通过旋转
0
度或180
度得到另一张多米诺骨牌,我们就认为这两张牌是等价的。形式上,
dominoes[i] = [a, b]
和dominoes[j] = [c, d]
等价的前提是a==c
且b==d
,或是a==d
且b==c
。在
0 <= i < j < dominoes.length
的前提下,找出满足dominoes[i]
和dominoes[j]
等价的骨牌对(i, j)
的数量。示例:
输入:dominoes = [[1,2],[2,1],[3,4],[5,6]] 输出:1
提示:
1 <= dominoes.length <= 40000
1 <= dominoes[i][j] <= 9
思路
用数组模拟哈希表进行计数
代码
class Solution:
def numEquivDominoPairs(self, dominoes: List[List[int]]) -> int:
nums = [0]*100
cnt = 0
for i, j in dominoes:
k = i*10+j if i>j else j*10+i
cnt += nums[k]
nums[k] += 1
return cnt
1.25
我的天,原来我鸽了4天吗
新的建模题太折磨了🤕
题目
959. 由斜杠划分区域
难度中等
在由 1 x 1 方格组成的 N x N 网格
grid
中,每个 1 x 1 方块由/
、\
或空格构成。这些字符会将方块划分为一些共边的区域。(请注意,反斜杠字符是转义的,因此
\
用"\\"
表示。)。返回区域的数目。
示例 1:
输入: [ " /", "/ " ] 输出:2 解释:2x2 网格如下:
示例 2:
输入: [ " /", " " ] 输出:1 解释:2x2 网格如下:
示例 3:
输入: [ "\\/", "/\\" ] 输出:4 解释:(回想一下,因为 \ 字符是转义的,所以 "\\/" 表示 \/,而 "/\\" 表示 /\。) 2x2 网格如下:
示例 4:
输入: [ "/\\", "\\/" ] 输出:5 解释:(回想一下,因为 \ 字符是转义的,所以 "/\\" 表示 /\,而 "\\/" 表示 \/。) 2x2 网格如下:
示例 5:
输入: [ "//", "/ " ] 输出:3 解释:2x2 网格如下:
提示:
1 <= grid.length == grid[0].length <= 30
grid[i][j]
是'/'
、'\'
、或' '
。
思路
参考了官方题解,是很巧妙的并查集解法
本身用并查集求连通度不难,但是要发现怎么去划分区域就有点…
借用官方的图来说,每个格子划分成4块,分别对应0, 1, 2, 3
我们可以把整个大的正方形抽象成一个4*N*N
的数组,数组的每个元素代表的是四分之一个小正方形
因此,我们的并查集大小也是4*N*N
,一开始假设都不是连通的
遍历grid
的矩阵,相当于我们每次对一个小正方形内的四块进行操作
每次要进行的有两个操作
- 小正方形内合并
- 小正方形间合并
针对正方形内合并
- 如果遍历到的是空格,即
‘ ’
,就把方块内四个格子全部连通 - 如果遍历到的是
/
,就把1和2连通,0和3连通 - 如果遍历到的是
\\
,就把0和1连通,2和3连通
针对正方形间合并
- 右侧合并
- 将当前块的1和右侧块的3合并
- 下侧合并
- 将当前块的2和下侧块的0合并
1.20
不知不觉都20号了
题目
628. 三个数的最大乘积
难度简单242
给定一个整型数组,在数组中找出由三个数组成的最大乘积,并输出这个乘积。
示例 1:
输入: [1,2,3] 输出: 6
示例 2:
输入: [1,2,3,4] 输出: 24
注意:
- 给定的整型数组长度范围是[3,104],数组中所有的元素范围是[-1000, 1000]。
- 输入的数组中任意三个数的乘积不会超出32位有符号整数的范围。
思路
今天的题可太简单了,先排序,然后最大的乘积只可能是以下两个乘积之一
- 最大的三个数的乘积
- 最小的两个数和最大的一个数的乘积
代码
class Solution:
def maximumProduct(self, nums: List[int]) -> int:
nums.sort()
res =[]
res.append(nums[0]*nums[1]*nums[-1])
res.append(nums[-1]*nums[-2]*nums[-3])
return max(res)
1.19
龙妈的三条龙可太折磨了。
题目
1584. 连接所有点的最小费用
难度中等
给你一个
points
数组,表示 2D 平面上的一些点,其中points[i] = [xi, yi]
。连接点
[xi, yi]
和点[xj, yj]
的费用为它们之间的 曼哈顿距离 :|xi - xj| + |yi - yj|
,其中|val|
表示val
的绝对值。请你返回将所有点连接的最小总费用。只有任意两点之间 有且仅有 一条简单路径时,才认为所有点都已连接。
示例 1:
输入:points = [[0,0],[2,2],[3,10],[5,2],[7,0]] 输出:20 解释: 我们可以按照上图所示连接所有点得到最小总费用,总费用为 20 。 注意到任意两个点之间只有唯一一条路径互相到达。
示例 2:
输入:points = [[3,12],[-2,5],[-4,1]] 输出:18
示例 3:
输入:points = [[0,0],[1,1],[1,0],[-1,1]] 输出:4
示例 4:
输入:points = [[-1000000,-1000000],[1000000,1000000]] 输出:4000000
示例 5:
输入:points = [[0,0]] 输出:0
提示:
1 <= points.length <= 1000
-106 <= xi, yi <= 106
- 所有点
(xi, yi)
两两不同。
思路
关键词:并查集,Kruskal算法,最小生成树
- 确定N个点之间所有可能的边
- 对所有边排序,升序排序
- 从最小的边开始遍历
- 如果遍历到的边连接到的是两个未连通的结点,则生成该条边
- 直到所有的点都连通
代码
class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
n = len(points)
parents = list(range(n))
def find(x):
if x != parents[x]:
parents[x] = find(parents[x])
return parents[x]
dist = []
for i in range(n-1):
x1,y1 = points[i]
for j in range(i+1,n):
x2,y2 = points[j]
dist.append([i,j,abs(x2-x1)+abs(y2-y1)])
dist.sort(key = lambda x: x[2])
# 构建最小生成树
edge, cost = 0, 0
for i,j,d in dist:
a, b = find(i), find(j)
if a != b:
# 两个点不连通
edge += 1
cost += d
parents[b] = a
if edge == n-1:
return cost
return cost
1.18
怎么寒假了还要这么头秃
题目
721. 账户合并
难度中等
给定一个列表
accounts
,每个元素accounts[i]
是一个字符串列表,其中第一个元素accounts[i][0]
是 名称 (name),其余元素是 emails 表示该账户的邮箱地址。现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。
合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。账户本身可以以任意顺序返回。
示例 1:
输入: accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]] 输出: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]] 解释: 第一个和第三个 John 是同一个人,因为他们有共同的邮箱地址 "johnsmith@mail.com"。 第二个 John 和 Mary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。 可以以任何顺序返回这些列表,例如答案 [['Mary','mary@mail.com'],['John','johnnybravo@mail.com'], ['John','john00@mail.com','john_newyork@mail.com','johnsmith@mail.com']] 也是正确的。
提示:
accounts
的长度将在[1,1000]
的范围内。accounts[i]
的长度将在[1,10]
的范围内。accounts[i][j]
的长度将在[1,30]
的范围内。
思路
我以为是单纯的两个哈希表能解决,结果一看题解又是并查集
-
一个哈希表存储:邮箱->index
-
另一个哈希表存储:邮箱->人名
利用第一个哈希表,把同一个人的邮箱用并查集连通在一起
比如 x@x.com
如果和 y@y.com
是同一个人的,那么就连结到一个公共的父节点上去(这里用index
表示父节点)
连接完之后,它们的index
就相同了
最后利用第二个哈希表,把index
相同的邮箱,放到同一个人名下,即可
代码
class Solution:
def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]:
email_to_index = dict()
email_to_name = dict()
for account in accounts:
name = account[0]
for email in account[1:]:
if email not in email_to_index:
email_to_index[email] = len(email_to_index)
email_to_name[email] = name
n = len(email_to_index)
parents = list(range(n))
def find(x):
if parents[x] != x:
parents[x] = find(parents[x])
return parents[x]
def union(x,y):
f_x, f_y = find(x), find(y)
if f_x != f_y:
parents[f_x] = f_y
# 开始合并同一个人的邮箱
for account in accounts:
first_index = email_to_index[account[1]]
for email in account[2:]:
union(first_index, email_to_index[email])
index_to_emails = collections.defaultdict(list)
for email, index in email_to_index.items():
index = find(index)
index_to_emails[index].append(email)
res = []
for emails in index_to_emails.values():
res.append([email_to_name[emails[0]]] + sorted(emails))
return res
1.17
赶紧刷完题建模去了(这次培训竟然第一道就是对龙妈的三条🐉去建模)
题目
1232. 缀点成线
难度简单
在一个 XY 坐标系中有一些点,我们用数组
coordinates
来分别记录它们的坐标,其中coordinates[i] = [x, y]
表示横坐标为x
、纵坐标为y
的点。请你来判断,这些点是否在该坐标系中属于同一条直线上,是则返回
true
,否则请返回false
。示例 1:
输入:coordinates = [[1,2],[2,3],[3,4],[4,5],[5,6],[6,7]] 输出:true
示例 2:
输入:coordinates = [[1,1],[2,2],[3,4],[4,5],[5,6],[7,7]] 输出:false
提示:
2 <= coordinates.length <= 1000
coordinates[i].length == 2
-10^4 <= coordinates[i][0], coordinates[i][1] <= 10^4
coordinates
中不含重复的点
思路
啥技巧都没用,就直接算斜率
代码
class Solution:
def checkStraightLine(self, coordinates: List[List[int]]) -> bool:
x0, y0 = coordinates[0]
x1, y1 = coordinates[1]
if x1 == x0:
for x,y in coordinates[1:]:
if x!=x0:
return False
return True
else:
k = (y1-y0)/(x1-x0)
for x,y in coordinates[2:]:
if x==x0: return False
elif (y-y0)/(x-x0) != k:
return False
return True
1.16
打砖块,并查集,困难,放假,CV
1.15
放 假 啦!
题目
947. 移除最多的同行或同列石头
难度中等
n
块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。
给你一个长度为
n
的数组stones
,其中stones[i] = [xi, yi]
表示第i
块石头的位置,返回 可以移除的石子 的最大数量。示例 1:
输入:stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]] 输出:5 解释:一种移除 5 块石头的方法如下所示: 1. 移除石头 [2,2] ,因为它和 [2,1] 同行。 2. 移除石头 [2,1] ,因为它和 [0,1] 同列。 3. 移除石头 [1,2] ,因为它和 [1,0] 同行。 4. 移除石头 [1,0] ,因为它和 [0,0] 同列。 5. 移除石头 [0,1] ,因为它和 [0,0] 同行。 石头 [0,0] 不能移除,因为它没有与另一块石头同行/列。
示例 2:
输入:stones = [[0,0],[0,2],[1,1],[2,0],[2,2]] 输出:3 解释:一种移除 3 块石头的方法如下所示: 1. 移除石头 [2,2] ,因为它和 [2,0] 同行。 2. 移除石头 [2,0] ,因为它和 [0,0] 同列。 3. 移除石头 [0,2] ,因为它和 [0,0] 同行。 石头 [0,0] 和 [1,1] 不能移除,因为它们没有与另一块石头同行/列。
示例 3:
输入:stones = [[0,0]] 输出:0 解释:[0,0] 是平面上唯一一块石头,所以不可以移除它。
提示:
1 <= stones.length <= 1000
0 <= xi, yi <= 104
- 不会有两块石头放在同一个坐标点上
思路
利用并查集,确定最后需要保留的点的个数
如果两个石头的行或者列相同,我们就可以认为它们是连通的
连通的石头意味着只需要留一块就够了
为了区分行和列,将行的值+10001
代码
class UnionFind:
def __init__(self,n):
self.dic = dict()
self.cnt = 0
def find(self, i):
if i not in self.dic:
self.dic[i] = i
self.cnt += 1
if self.dic[i] != i:
self.dic[i] = self.find(self.dic[i])
return self.dic[i]
def union(self, x, y):
f_x, f_y = self.find(x), self.find(y)
if f_x == f_y:
return
self.dic[f_x] = f_y
self.cnt -= 1
class Solution:
def removeStones(self, stones: List[List[int]]) -> int:
n = len(stones)
uf = UnionFind(n)
for i, j in stones:
uf.union(i+10001, j)
return n - uf.cnt
1.14
最后一门OS,给爷🐛!
题目
1018. 可被 5 整除的二进制前缀
难度简单
给定由若干
0
和1
组成的数组A
。我们定义N_i
:从A[0]
到A[i]
的第i
个子数组被解释为一个二进制数(从最高有效位到最低有效位)。返回布尔值列表
answer
,只有当N_i
可以被5
整除时,答案answer[i]
为true
,否则为false
。示例 1:
输入:[0,1,1] 输出:[true,false,false] 解释: 输入数字为 0, 01, 011;也就是十进制中的 0, 1, 3 。只有第一个数可以被 5 整除,因此 answer[0] 为真。
示例 2:
输入:[1,1,1] 输出:[false,false,false]
示例 3:
输入:[0,1,1,1,1,1] 输出:[true,false,false,false,true,false]
示例 4:
输入:[1,1,1,0,1] 输出:[false,false,false,false,false]
提示:
1 <= A.length <= 30000
A[i]
为0
或1
思路
全程只算余数即可
代码
class Solution:
def prefixesDivBy5(self, A: List[int]) -> List[bool]:
res = []
tmp = 0
for i in A:
tmp *= 2
tmp += i
tmp %= 5
if tmp == 0:
res.append(True)
else:
res.append(False)
return res
1.13
又是并查集😓
明天考OS,紧张到起飞
题目
684. 冗余连接
难度中等
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以
边
组成的二维数组。每一个边
的元素是一对[u, v]
,满足u < v
,表示连接顶点u
和v
的无向图的边。返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边
[u, v]
应满足相同的格式u < v
。示例 1:
输入: [[1,2], [1,3], [2,3]] 输出: [2,3] 解释: 给定的无向图为: 1 / \ 2 - 3
示例 2:
输入: [[1,2], [2,3], [3,4], [1,4], [1,5]] 输出: [1,4] 解释: 给定的无向图为: 5 - 1 - 2 | | 4 - 3
注意:
- 输入的二维数组大小在 3 到 1000。
- 二维数组中的整数在1到N之间,其中N是输入数组的大小。
思路
用并查集确认两个结点是否连通(parent相同),如果在已经连通的情况下又有一条边将其相连,则说明产生了环路,返回这条边即可
代码
class Solution:
def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
# 并查集
n = len(edges)
parents = list(range(n+1))
def find(i):
if parents[i] != i:
parents[i] = find(parents[i])
return parents[i]
def union(x,y):
fx, fy = find(x), find(y)
if fx!=fy:
parents[fx] = fy
for x,y in edges:
if find(x) == find(y):
return [x,y]
union(x,y)
1.12
图论,拓扑排序,困难题,cv
1.11
a lovely Monday.
题目
1202. 交换字符串中的元素
难度中等
给你一个字符串
s
,以及该字符串中的一些「索引对」数组pairs
,其中pairs[i] = [a, b]
表示字符串中的两个索引(编号从 0 开始)。你可以 任意多次交换 在
pairs
中任意一对索引处的字符。返回在经过若干次交换后,
s
可以变成的按字典序最小的字符串。示例 1:
输入:s = "dcab", pairs = [[0,3],[1,2]] 输出:"bacd" 解释: 交换 s[0] 和 s[3], s = "bcad" 交换 s[1] 和 s[2], s = "bacd"
示例 2:
输入:s = "dcab", pairs = [[0,3],[1,2],[0,2]] 输出:"abcd" 解释: 交换 s[0] 和 s[3], s = "bcad" 交换 s[0] 和 s[2], s = "acbd" 交换 s[1] 和 s[2], s = "abcd"
示例 3:
输入:s = "cba", pairs = [[0,1],[1,2]] 输出:"abc" 解释: 交换 s[0] 和 s[1], s = "bca" 交换 s[1] 和 s[2], s = "bac" 交换 s[0] 和 s[1], s = "abc"
提示:
1 <= s.length <= 10^5
0 <= pairs.length <= 10^5
0 <= pairs[i][0], pairs[i][1] < s.length
s
中只含有小写英文字母
题目
初步复习了一下并查集(没有跟官方题解一样用按秩优化)
根据题目要求可以很快发现有一个连通子集的问题,比如[0,1]
和[1,3]
可以理解为在一个连通集合里
因此考虑用并查集实现快速地查询哪些元素是在一个集合里的
具体思路见代码,以及模板题解
代码实现使用的是递归的路径压缩(我还发现直接把并查集写在Solution里会报递归深度错误)
代码
class DSU:
def __init__(self, n:int):
self.p = [i for i in range(n)]
def find(self, x: int):
if x != self.p[x]:
self.p[x] = self.find(self.p[x])
return self.p[x]
def merge(self, x:int , y:int):
fx, fy = self.find(x), self.find(y)
if fx == fy: return
self.p[fx] = self.p[fy]
return
class Solution:
def smallestStringWithSwaps(self, s: str, pairs: List[List[int]]) -> str:
# 构造并查集
length = len(s)
dsu = DSU(length)
for i,j in pairs:
dsu.merge(i,j)
dic = collections.defaultdict(list)
for i in range(length):
dic[dsu.find(i)].append(i)
res = list(range(length))
for v in dic.values():
words = [s[i] for i in v]
words.sort()
cnt = 0
v.sort()
for i in v:
res[i] = words[cnt]
cnt += 1
return "".join(res)
1.10
228. 汇总区间
难度简单
给定一个无重复元素的有序整数数组
nums
。返回 恰好覆盖数组中所有数字 的 最小有序 区间范围列表。也就是说,
nums
的每个元素都恰好被某个区间范围所覆盖,并且不存在属于某个范围但不属于nums
的数字x
。列表中的每个区间范围
[a,b]
应该按如下格式输出:
"a->b"
,如果a != b
"a"
,如果a == b
示例 1:
输入:nums = [0,1,2,4,5,7] 输出:["0->2","4->5","7"] 解释:区间范围是: [0,2] --> "0->2" [4,5] --> "4->5" [7,7] --> "7"
示例 2:
输入:nums = [0,2,3,4,6,8,9] 输出:["0","2->4","6","8->9"] 解释:区间范围是: [0,0] --> "0" [2,4] --> "2->4" [6,6] --> "6" [8,9] --> "8->9"
示例 3:
输入:nums = [] 输出:[]
示例 4:
输入:nums = [-1] 输出:["-1"]
示例 5:
输入:nums = [0] 输出:["0"]
提示:
0 <= nums.length <= 20
-231 <= nums[i] <= 231 - 1
nums
中的所有值都 互不相同nums
按升序排列
思路
没有技术可言,就是遍历一遍数组,把所有连续的子数组转换成x->y
的字符串即可
代码
class Solution:
def summaryRanges(self, nums: List[int]) -> List[str]:
if not nums:
return []
if len(nums) == 1:
return [str(nums[0])]
first, last = nums[0], nums[0]
res = []
for i,num in enumerate(nums[1:]):
if num != last + 1:
if first == last:
res.append(str(first))
else:
res.append(str(first)+"->"+str(last))
first = num
last = num
if i == len(nums)-2:
if first == last:
res.append(str(last))
else:
res.append(str(first)+"->"+str(last))
return res
1.9
@labuladong!yyds!
题解
123. 买卖股票的最佳时机 III
难度困难
给定一个数组,它的第
i
个元素是一支给定的股票在第i
天的价格。设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
**注意:**你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4] 输出:6 解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。 随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5] 输出:4 解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。 注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。 因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1] 输出:0
提示:
1 <= prices.length <= 105
0 <= prices[i] <= 105
思路
考完五门,还剩两门,计网和OS,还有四天,算是有空写题解了
看完东哥的算法小抄,真就直接秒杀动归呗。
一般来说,如果不限交易次数,只需要设置两个变量dp0
和dp1
分别表示当天未持有股票的状态和持有股票的状态。
这道题要求最多只能交易两次,所以我们的两个变量变成了两个数组
DP Table
-
dp0[i]
表示当天未持有股票的最大利润,且已进行了i
次交易(卖出过i
次股票) -
dp1[i]
表示当天持有股票的最大利润,且进行了i
次交易
Base case
dp0 = [0,0,0]
,因为一开始未持有股票,所以第一天无论是交易了几次,最大利润都是0dp1 = [-float('inf'),-float('inf')]
,因为一开始不可能持有股票,所以我们初始化最大利润为负无穷(避免第一天就能卖股票)
状态转移方程
- 针对
dp0
而言- 要么就是当天卖出了股票,要么就是本来就没有股票
dp0[0]
永远为0,因为交易次数为0且未持有股票,说明从未购入股票dp0[1]
为前一天未持有股票的最大收益,或者前一天持有股票且当天卖出股票的最大收益- 注意这里如果是保持未持有股票,则交易次数是不会变的
- 如果是卖出股票,那必定是根据
dp1[0]
去更新的,因为更新后才变成dp0[1]
所表示的1次交易
dp0[2]
和dp0[1]
同理
- 针对
dp1
而言- 要么就是当天买入了股票,要么就是之前就持有了股票,还没卖
dp1[0]
就是根据前一天已经持有股票的最大利润dp1[0]
,和当天买入股票的最大利润dp0[0]-price
dp1[1]
同理,需要考虑交易次数
最后一天未持有股票,且交易次数为2的情况下必然是最大利益,即
dp0[2]
代码
class Solution:
def maxProfit(self, prices: List[int]) -> int:
dp0 = [0,0,0] # 表示当天未持有股票的情况
dp1 = [-float('inf'), -float('inf')] # 表示当天持有股票的情况
for price in prices:
dp0[1] = max(dp1[0]+price, dp0[1])
dp0[2] = max(dp1[1]+price, dp0[2])
dp1[0] = max(dp0[0]-price, dp1[0])
dp1[1] = max(dp0[1]-price, dp1[1])
return dp0[2]
1.8
软测老师捞捞我
题目
189. 旋转数组
难度中等
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3 输出: [5,6,7,1,2,3,4] 解释: 向右旋转 1 步: [7,1,2,3,4,5,6] 向右旋转 2 步: [6,7,1,2,3,4,5] 向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2 输出: [3,99,-1,-100] 解释: 向右旋转 1 步: [99,-1,-100,3] 向右旋转 2 步: [3,99,-1,-100]
说明:
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的 原地 算法。
代码
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
n = len(nums)
k %= n
def reverse(i,j):
# 翻转数组的[i:j]
left, right = i, j-1
while left<right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
reverse(0,n)
reverse(0,k)
reverse(k,n)
1.7
真的复习不完了兄弟萌
题目
547. 省份数量
难度中等
有
n
个城市,其中一些彼此相连,另一些没有相连。如果城市a
与城市b
直接相连,且城市b
与城市c
直接相连,那么城市a
与城市c
间接相连。省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个
n x n
的矩阵isConnected
,其中isConnected[i][j] = 1
表示第i
个城市和第j
个城市直接相连,而isConnected[i][j] = 0
表示二者不直接相连。返回矩阵中 省份 的数量。
示例 1:
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]] 输出:2
示例 2:
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]] 输出:3
提示:
1 <= n <= 200
n == isConnected.length
n == isConnected[i].length
isConnected[i][j]
为1
或0
isConnected[i][i] == 1
isConnected[i][j] == isConnected[j][i]
代码
class Solution:
def findCircleNum(self, isConnected: List[List[int]]) -> int:
def dfs(i):
for j in range(n):
if isConnected[i][j] == 1 and j not in visited:
visited.add(j)
dfs(j)
visited = set()
circle = 0
n = len(isConnected)
for i in range(n):
if i not in visited:
dfs(i)
circle += 1
return circle
题目
1.6
cv的,没空复习并查集了
题目
399. 除法求值
难度中等293
给你一个变量对数组
equations
和一个实数值数组values
作为已知条件,其中equations[i] = [Ai, Bi]
和values[i]
共同表示等式Ai / Bi = values[i]
。每个Ai
或Bi
是一个表示单个变量的字符串。另有一些以数组
queries
表示的问题,其中queries[j] = [Cj, Dj]
表示第j
个问题,请你根据已知条件找出Cj / Dj = ?
的结果作为答案。返回 所有问题的答案 。如果存在某个无法确定的答案,则用
-1.0
替代这个答案。**注意:**输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
示例 1:
输入:equations = [["a","b"],["b","c"]], values = [2.0,3.0], queries = [["a","c"],["b","a"],["a","e"],["a","a"],["x","x"]] 输出:[6.00000,0.50000,-1.00000,1.00000,-1.00000] 解释: 条件:a / b = 2.0, b / c = 3.0 问题:a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? 结果:[6.0, 0.5, -1.0, 1.0, -1.0 ]
示例 2:
输入:equations = [["a","b"],["b","c"],["bc","cd"]], values = [1.5,2.5,5.0], queries = [["a","c"],["c","b"],["bc","cd"],["cd","bc"]] 输出:[3.75000,0.40000,5.00000,0.20000]
示例 3:
输入:equations = [["a","b"]], values = [0.5], queries = [["a","b"],["b","a"],["a","c"],["x","y"]] 输出:[0.50000,2.00000,-1.00000,-1.00000]
提示:
1 <= equations.length <= 20
equations[i].length == 2
1 <= Ai.length, Bi.length <= 5
values.length == equations.length
0.0 < values[i] <= 20.0
1 <= queries.length <= 20
queries[i].length == 2
1 <= Cj.length, Dj.length <= 5
Ai, Bi, Cj, Dj
由小写英文字母与数字组成
代码
class Solution:
def calcEquation(self, equations: List[List[str]], values: List[float], queries: List[List[str]]) -> List[float]:
# 构造图,equations的第一项除以第二项等于value里的对应值,第二项除以第一项等于其倒数
graph = {}
for (x, y), v in zip(equations, values):
if x in graph:
graph[x][y] = v
else:
graph[x] = {y: v}
if y in graph:
graph[y][x] = 1/v
else:
graph[y] = {x: 1/v}
# dfs找寻从s到t的路径并返回结果叠乘后的边权重即结果
def dfs(s, t) -> int:
if s not in graph:
return -1
if t == s:
return 1
for node in graph[s].keys():
if node == t:
return graph[s][node]
elif node not in visited:
visited.add(node) # 添加到已访问避免重复遍历
v = dfs(node, t)
if v != -1:
return graph[s][node]*v
return -1
# 逐个计算query的值
res = []
for qs, qt in queries:
visited = set()
res.append(dfs(qs, qt))
return res
1.5
学校的课堂知识明明以后都用不上,却还是要为了绩点去复习
能直接快进到寒假不,我想快点开始学 JS 了
题目
830. 较大分组的位置
难度简单
在一个由小写字母构成的字符串
s
中,包含由一些连续的相同字符所构成的分组。例如,在字符串
s = "abbxxxxzyy"
中,就含有"a"
,"bb"
,"xxxx"
,"z"
和"yy"
这样的一些分组。分组可以用区间
[start, end]
表示,其中start
和end
分别表示该分组的起始和终止位置的下标。上例中的"xxxx"
分组用区间表示为[3,6]
。我们称所有包含大于或等于三个连续字符的分组为 较大分组 。
找到每一个 较大分组 的区间,按起始位置下标递增顺序排序后,返回结果。
示例 1:
输入:s = "abbxxxxzzy" 输出:[[3,6]] 解释:"xxxx" 是一个起始于 3 且终止于 6 的较大分组。
示例 2:
输入:s = "abc" 输出:[] 解释:"a","b" 和 "c" 均不是符合要求的较大分组。
示例 3:
输入:s = "abcdddeeeeaabbbcd" 输出:[[3,5],[6,9],[12,14]] 解释:较大分组为 "ddd", "eeee" 和 "bbb"
示例 4:
输入:s = "aba" 输出:[]
提示:
1 <= s.length <= 1000
s
仅含小写英文字母
代码
class Solution:
def largeGroupPositions(self, s: str) -> List[List[int]]:
last, cnt = "", 0
res = []
for i, word in enumerate(s):
if word == last:
cnt += 1
else:
if cnt>=3:
res.append([i-cnt,i-1])
cnt = 1
last = word
if cnt>=3:
res.append([len(s)-cnt,len(s)-1])
return res
1.4
考试周开始了 🙌
题目
509. 斐波那契数
难度简单
斐波那契数,通常用
F(n)
表示,形成的序列称为 斐波那契数列 。该数列由0
和1
开始,后面的每一项数字都是前面两项数字的和。也就是:F(0) = 0,F(1) = 1 F(n) = F(n - 1) + F(n - 2),其中 n > 1
给你
n
,请计算F(n)
。
代码
class Solution:
def fib(self, N: int) -> int:
dp = [0,1]
for i in range(1,N):
dp.append(dp[-1]+dp[-2])
return dp[N]
1.3
还挺简单的,俺复习去了
题目
86. 分隔链表
难度中等
给你一个链表和一个特定值
x
,请你对链表进行分隔,使得所有小于x
的节点都出现在大于或等于x
的节点之前。你应当保留两个分区中每个节点的初始相对位置。
示例:
输入:head = 1->4->3->2->5->2, x = 3 输出:1->2->2->4->3->5
代码
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def partition(self, head: ListNode, x: int) -> ListNode:
node = head
dummy_small, dummy_large = ListNode(0), ListNode(0)
node_small, node_large = dummy_small, dummy_large
while node:
nxt_node = node.next
if node.val<x:
if node_small==dummy_small:
node_small = node
dummy_small.next = node_small
else:
node_small.next = node
node_small = node
node_small.next = None
else:
if node_large == dummy_large:
node_large = node
dummy_large.next = node_large
else:
node_large.next = node
node_large = node
node_large.next = None
node = nxt_node
node_small.next = dummy_large.next
return dummy_small.next
1.2
好家伙,昨天做完题直接忘记写了
题目
239. 滑动窗口最大值
难度困难
给你一个整数数组
nums
,有一个大小为k
的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的k
个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值。
示例 1:
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3 输出:[3,3,5,5,6,7] 解释: 滑动窗口的位置 最大值 --------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
示例 2:
输入:nums = [1], k = 1 输出:[1]
示例 3:
输入:nums = [1,-1], k = 1 输出:[1,-1]
示例 4:
输入:nums = [9,11], k = 2 输出:[11]
示例 5:
输入:nums = [4,-2], k = 2 输出:[4]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
1 <= k <= nums.length
代码
堆栈:时间复杂度O(NlogN)
import heapq
class Solution:
def maxSlidingWindow(self, nums, k):
heap = [(-nums[i], i) for i in range(k)]
heapq.heapify(heap)
res = [-heap[0][0]]
for i in range(k,len(nums)):
heapq.heappush(heap, (-nums[i], i))
while heap[0][1] <= i - k:
heapq.heappop(heap)
res.append(-heap[0][0])
return res
双向队列:时间复杂度O(N)
from collections import deque
class Solution:
def maxSlidingWindow(self, nums, k):
n = len(nums)
q = deque()
for i in range(k):
while q and nums[i] >= nums[q[-1]]:
q.pop()
q.append(i)
res = [nums[q[0]]]
for i in range(k, n):
while q and nums[i] >= nums[q[-1]]:
q.pop()
q.append(i)
while q[0] <= i - k:
q.popleft()
res.append(nums[q[0]])
return res
1.1
因为太简单而忘记更新 😓
题目
605. 种花问题
难度简单
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组
flowerbed
表示花坛,由若干0
和1
组成,其中0
表示没种植花,1
表示种植了花。另有一个数n
,能否在不打破种植规则的情况下种入n
朵花?能则返回true
,不能则返回false
。示例 1:
输入:flowerbed = [1,0,0,0,1], n = 1 输出:true
示例 2:
输入:flowerbed = [1,0,0,0,1], n = 2 输出:false
提示:
1 <= flowerbed.length <= 2 * 104
flowerbed[i]
为0
或1
flowerbed
中不存在相邻的两朵花0 <= n <= flowerbed.length
代码
class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
cnt = 0
index = 0
size = len(flowerbed)
while index<size:
if flowerbed[index] == 1:
# 该位置有花
index += 2
elif index+1<size and flowerbed[index+1] == 1:
# 后一个位置有花
index += 3
elif index-1>=0 and flowerbed[index-1] == 1:
# 前一个位置有花
index += 1
else:
# 前中后均无花
flowerbed[index] = 1
cnt += 1
index += 2
return cnt>=n