LeetCode 第 193 场周赛
一维数组的动态和
给你一个数组 nums 。数组「动态和」的计算公式为:runningSum[i] = sum(nums[0]…nums[i]) 。
请返回 nums 的动态和。
思路
前缀和模板即可
class Solution:
def runningSum(self, nums: List[int]) -> List[int]:
ret = [0] * len(nums)
for idx, val in enumerate(nums):
if idx == 0:
ret[idx] += val
else:
ret[idx] = val + ret[idx - 1]
return ret
不同整数的最少数目
给你一个整数数组 arr 和一个整数 k 。现需要从数组中恰好移除 k 个元素,请找出移除后数组中不同整数的最少数目。
示例:
输入:arr = [4,3,1,1,3,3,2], k = 3
输出:2
解释:先移除 4、2 ,然后再移除两个 1 中的任意 1 个或者三个 3 中的任意 1 个,最后剩下 1 和 3 两种整数。
思路
统计每个元素和出现次数, 按照出现次数从小到大排序, 然后删除 k 个元素即可.
优化: 可以通过小根堆优化. 简单来说, 如果当前剩余可移除元素个数大于堆顶的数字, 那么弹出, 燃火更新当前可移除元素个数, 最后统计堆的规模即可.
我没有考虑到优化, 直接放原始的代码
class Solution:
def findLeastNumOfUniqueInts(self, arr: List[int], k: int) -> int:
from collections import Counter
counter = Counter(arr)
kv = sorted(counter.items(), key = lambda x: x[1])
ret = 0
for idx, (key, v) in enumerate(kv):
print(key, v)
if v <= k:
k -= v
else:
ret += 1
return ret
制作 m 束花所需的最少天数
给你一个整数数组 bloomDay,以及两个整数 m 和 k 。
现需要制作 m 束花。制作花束时,需要使用花园中 相邻的 k 朵花 。
花园中有 n 朵花,第 i 朵花会在 bloomDay[i] 时盛开,恰好 可以用于 一束 花中。
请你返回从花园中摘 m 束花需要等待的最少的天数。如果不能摘到 m 束花则返回 -1 。
示例:
输入:bloomDay = [1,10,3,10,2], m = 3, k = 1
输出:3
解释:让我们一起观察这三天的花开过程,x 表示花开,而 _ 表示花还未开。
现在需要制作 3 束花,每束只需要 1 朵。
1 天后:[x, _, _, _, _] // 只能制作 1 束花
2 天后:[x, _, _, _, x] // 只能制作 2 束花
3 天后:[x, _, x, _, x] // 可以制作 3 束花,答案为 3
输入:bloomDay = [7,7,7,7,12,7,7], m = 2, k = 3
输出:12
解释:要制作 2 束花,每束需要 3 朵。
花园在 7 天后和 12 天后的情况如下:
7 天后:[x, x, x, x, _, x, x]
可以用前 3 朵盛开的花制作第一束花。但不能使用后 3 朵盛开的花,因为它们不相邻。
12 天后:[x, x, x, x, x, x, x]
显然,我们可以用不同的方式制作两束花。
思路
开始感觉是动态规划问题, 在给定数组里面找 m 个长为 k 的子数组, 然后超时了.
一个思路是枚举等待的天数, 从 1 开始, 当找到第一个符合要求的天数时, 肯定是最小的. 给定天数, 遍历数组就可以找到对应天数可以制作多少束花. 这个思路可以用二分来优化. 假设数组有 k 个元素, 数组中元素最大值为 n, 那么时间复杂度为
O
(
n
log
(
k
)
)
O(n\log(k))
O(nlog(k))
代码如下:
class Solution:
def minDays(self, bloomDay: List[int], m: int, k: int) -> int:
def isValid(num):
cnt = 0
ret = 0
for val in bloomDay:
if val <= num:
cnt += 1
else:
cnt = 0
if cnt == k:
ret += 1
cnt = 0
if ret == m:
return True
return False
lo, hi = 0, max(bloomDay)
while lo <= hi:
mid = (lo + hi) // 2
if isValid(mid):
hi = mid - 1
else:
lo = mid + 1
if isValid(hi + 1): return hi + 1
return -1
树节点的第 K 个祖先
给你一棵树,树上有 n 个节点,按从 0 到 n-1 编号。树以父节点数组的形式给出,其中 parent[i] 是节点 i 的父节点。树的根节点是编号为 0 的节点。
请你设计并实现 getKthAncestor(int node, int k) 函数,函数返回节点 node 的第 k 个祖先节点。如果不存在这样的祖先节点,返回 -1 。
树节点的第 k 个祖先节点是从该节点到根节点路径上的第 k 个节点。
示例:
输入:
["TreeAncestor","getKthAncestor","getKthAncestor","getKthAncestor"]
[[7,[-1,0,0,1,1,2,2]],[3,1],[5,2],[6,3]]
输出:
[null,1,0,-1]
解释:
TreeAncestor treeAncestor = new TreeAncestor(7, [-1, 0, 0, 1, 1, 2, 2]);
treeAncestor.getKthAncestor(3, 1); // 返回 1 ,它是 3 的父节点
treeAncestor.getKthAncestor(5, 2); // 返回 0 ,它是 5 的祖父节点
treeAncestor.getKthAncestor(6, 3); // 返回 -1 因为不存在满足要求的祖先节点
思路
这是公共祖先问题的简化版本, 相关知识为倍增法. 如果常规方法来查, 比如查某个节点的第n个祖先节点, 那么需要查找 n 次, 多次查找时间复杂度过高.
例如给定 n=15, 常规方法要查 15 次, 但是有一种思路是这样: 先找到他的15 = 8 + 4 + 2 + 1, 那么先找到原始节点的第 8 个祖先节点, 在找其第 4 个祖先节点, 然后第 2 个, 第 1 个, 这样只需查找 4 次, 查找的复杂度就降下来了.
可以在写入数组的时候, 构造查找表pre[i][j]
表示第 i 个节点的第
2
j
2^j
2j 个祖先节点是谁 其递推关系为
p
r
e
[
i
]
[
j
]
=
p
r
e
[
p
r
e
[
i
]
[
j
−
1
]
]
[
j
−
1
]
pre[i][j] = pre\left[pre[i][j- 1]\right][j - 1]
pre[i][j]=pre[pre[i][j−1]][j−1]
例如, 第2 个节点的第 16(j - 1) 个祖先节点, 为第 2 个节点的第 8 个(i, j - 1)祖先的第 8(j - 1)个祖先
建表的时候, 时间复杂度是
O
(
n
log
(
n
)
)
O(n\log(n))
O(nlog(n))
但查询快了很多
代码如下:
class TreeAncestor:
def __init__(self, n: int, parent: List[int]):
pre = [[-1] * 16 for _ in range(n)]
for i, pi in enumerate(parent):
pre[i][0] = pi
for j in range(1, 16):
for i in range(n):
if pre[i][j - 1] == -1:
continue
pre[i][j] = pre[pre[i][j - 1]][j - 1]
self.pre = pre
def getKthAncestor(self, node: int, k: int) -> int:
for i in range(15, -1, -1):
if (k >> i) & 1:
node = self.pre[node][i]
if node == -1:
break
return node