[Leetcode Weekly Contest]312
链接:LeetCode
[Leetcode]2418. 按身高排序
给你一个字符串数组 names ,和一个由 互不相同 的正整数组成的数组 heights 。两个数组的长度均为 n 。
对于每个下标 i,names[i] 和 heights[i] 表示第 i 个人的名字和身高。
请按身高 降序 顺序返回对应的名字数组 names 。
由于height各不相同,通过哈希存储即可。
class Solution {
public String[] sortPeople(String[] names, int[] heights) {
HashMap<Integer, String> height2Name = new HashMap<>();
int n = names.length;
for(int i=0;i<n;++i) {
height2Name.put(heights[i], names[i]);
}
Arrays.sort(heights);
var res = new String[n];
for(int i=n-1;i>=0;--i) {
res[n-1-i] = height2Name.get(heights[i]);
}
return res;
}
}
[Leetcode]2419. 按位与最大的最长子数组
给你一个长度为 n 的整数数组 nums 。
考虑 nums 中进行 按位与(bitwise AND)运算得到的值 最大 的 非空 子数组。
- 换句话说,令 k 是 nums 任意 子数组执行按位与运算所能得到的最大值。那么,只需要考虑那些执行一次按位与运算后等于 k 的子数组。
返回满足要求的 最长 子数组的长度。
数组的按位与就是对数组中的所有数字进行按位与运算。
子数组 是数组中的一个连续元素序列。
由于 AND 不会让数字变大,那么最大值就是数组的最大值。因此题目实际上求的是数组中的最大值最多连续出现了几次。复杂度\(\mathcal{O}(n)\)。
class Solution {
public int longestSubarray(int[] nums) {
int cur = 0, res = 0;
int mx = Arrays.stream(nums).max().getAsInt();
for(var num:nums) {
if(num == mx) {
cur ++;
res = Math.max(res, cur);
}
else {
cur = 0;
}
}
return res;
}
}
[Leetcode]2420. 找到所有好下标
给你一个大小为 n 下标从 0 开始的整数数组 nums 和一个正整数 k 。
对于 k <= i < n - k 之间的一个下标 i ,如果它满足以下条件,我们就称它为一个 好 下标:
- 下标 i 之前 的 k 个元素是 非递增的 。
- 下标 i 之后 的 k 个元素是 非递减的 。
按 升序 返回所有好下标。
先倒着遍历,得到从每个位置向后的最长连续非降序列的长度,然后正着遍历,得到每个位置向前的最长连续非增序列的长度,同时统计答案。
class Solution:
def goodIndices(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
ans = []
dec = [1] * n
for i in range(n - 2, k, -1):
if nums[i] <= nums[i + 1]:
dec[i] = dec[i + 1] + 1 # 递推
inc = 1
for i in range(1, n - k):
if inc >= k and dec[i + 1] >= k:
ans.append(i)
if nums[i - 1] >= nums[i]:
inc += 1 # 递推
else:
inc = 1
return ans
[Leetcode] 2421. 好路径的数目
给你一棵 n 个节点的树(连通无向无环的图),节点编号从 0 到 n - 1 且恰好有 n - 1 条边。
给你一个长度为 n 下标从 0 开始的整数数组 vals ,分别表示每个节点的值。同时给你一个二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示节点 ai 和 bi 之间有一条 无向 边。
一条 好路径 需要满足以下条件:
- 开始节点和结束节点的值 相同 。
- 开始节点和结束节点中间的所有节点值都 小于等于 开始节点的值(也就是说开始节点的值应该是路径上所有节点的最大值)。
请你返回不同好路径的数目。
注意,一条路径和它反向的路径算作 同一 路径。比方说, 0 -> 1 与 1 -> 0 视为同一条路径。单个节点也视为一条合法路径。
并查集。按节点值从小到大考虑,同时用并查集合并时,总是从节点值小的点往节点值大的点合并,这样可以保证连通块的代表元的节点值是最大的。
对于节点 x 及其邻居 y,如果 y 所处的连通分量的最大节点值不超过 \(\textit{vals}[x]\),那么可以把 y 所处的连通块合并到 x 所处的连通块中。
如果此时这两个连通块的最大节点值相同,那么可以根据乘法原理,把这两个连通块内的等于最大节点值的节点个数相乘,加到答案中。
class Solution {
int[] fa;
public int numberOfGoodPaths(int[] vals, int[][] edges) {
var n = vals.length;
List<Integer>[] g = new ArrayList[n];
Arrays.setAll(g, e -> new ArrayList<>());
for (var e : edges) {
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x); // 建图
}
fa = new int[n];
for (var i = 0; i < n; i++) fa[i] = i;
// size[x] 表示节点值等于 vals[x] 的节点个数,如果按照节点值从小到大合并,size[x] 也是连通块内的等于最大节点值的节点个数
var size = new int[n];
Arrays.fill(size, 1);
var id = IntStream.range(0, n).boxed().toArray(Integer[]::new);
Arrays.sort(id, (i, j) -> vals[i] - vals[j]);
var ans = n;
for (var x : id) {
int vx = vals[x], fx = find(x);
for (var y : g[x]) {
y = find(y);
if (y == fx || vals[y] > vx) continue; // 只考虑最大节点值比 vx 小的连通块
if (vals[y] == vx) { // 可以构成好路径
ans += size[fx] * size[y]; // 乘法原理
size[fx] += size[y]; // 统计连通块内节点值等于 vx 的节点个数
}
fa[y] = fx; // 把小的节点值合并到大的节点值上
}
}
return ans;
}
int find(int x) {
if (fa[x] != x) fa[x] = find(fa[x]);
return fa[x];
}
}
参考:
LeetCode