目录
一,3370. 仅含置位位的最小整数
二,3371. 识别数组中的最大异常值
三,3372. 连接两棵树后最大目标节点数目 I
四,3373. 连接两棵树后最大目标节点数目 II
一,3370. 仅含置位位的最小整数
两种做法:
- 暴力枚举长度为 1,2,3..... 的二进制全为 1 的值,直到它大于等于 n
- 直接返回长度为 m (m = n 的二进制长度) 的二进制全为 1 的值
代码如下:
//暴力枚举 O(logn)
class Solution {
public int smallestNumber(int n) {
int ans = 0, i = 0;
while(ans < n){
ans = (1 << i) - 1;
i++;
}
return ans;
}
}
//O(1)
class Solution {
public int smallestNumber(int n) {
int len = 32 - Integer.numberOfLeadingZeros(n);
return (1 << len) - 1;
}
}
二,3371. 识别数组中的最大异常值
本题其实是一道数学问题,设特殊数字的和为 x,异常值为 y,由题意得,2*x + y = sum(nums),这时可以看出,它实际上就是 leetcode 第一题两数之和,可以枚举 x,通过上述公式得到 y,判断其是否在 nums 数组中,如果在,求出其中的最大值。
代码如下:
class Solution {
public int getLargestOutlier(int[] nums) {
int n = nums.length, s = 0;
Map<Integer, Integer> map = new HashMap<>();
for(int x : nums){
map.merge(x, 1, Integer::sum);
s += x;
}
int ans = -1001;
for(int y : nums){
int t = s - y;
//需要特判 x == y 的情况,这时 map.get(t / 2) > 1
if(t%2==0 && map.containsKey(t/2) && (t/2 != y || map.get(t/2) > 1))
ans = Math.max(ans, y);
}
return ans;
}
}
三,3372. 连接两棵树后最大目标节点数目 I
对于第一棵树中的第 i 个节点来说,新添加的边其中的一个节点必定是节点 i,因为如果不是 i,第二棵树中的每个节点与 i 节点的距离只会更大。
得出上述结论后,我们可以发现其实第二棵树中满足条件的节点个数是固定的,直接暴力枚举每一个与 i 相连的节点,求出与 i 节点距离小于等于 k - 1 (i 与 枚举的节点之间还有一条边)的最多节点数 mx。
这时,只需要求出第一棵树中第 i 节点满足条件的目标节点个数 + mx,就得到了 ans[i].
代码如下:
class Solution {
public int[] maxTargetNodes(int[][] edges1, int[][] edges2, int k) {
List<Integer>[] g1 = createTree(edges1);
List<Integer>[] g2 = createTree(edges2);
int n = edges1.length + 1;
int m = edges2.length + 1;
int mx = 0;
for(int i=0; i<m; i++){
mx = Math.max(mx, dfs(i, -1, k-1, g2) + (k==0?0:1));
}
int[] ans = new int[n];
for(int i=0; i<n; i++){
//与i距离<=k的节点数 + 本身 + 第二棵树中的节点个数
ans[i] = dfs(i, -1, k, g1) + 1 + mx;
}
return ans;
}
int dfs(int x, int fa, int k, List<Integer>[] g){
if(k <= 0) return 0;
int res = 0;
for(int y : g[x]){
if(y != fa){
res += dfs(y, x, k-1, g) + 1;
}
}
return res;
}
List<Integer>[] createTree(int[][] edge){
int n = edge.length + 1;
List<Integer>[] g = new ArrayList[n];
Arrays.setAll(g, e -> new ArrayList<>());
for(int[] e : edge){
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
return g;
}
}
四,3373. 连接两棵树后最大目标节点数目 II
对于每棵树来说,假定一个根节点 root,分别计算出与 root 之间路径为奇,偶的节点数量 x, y(x + y = n),如果把 root 节点相邻的节点 reroot 当成根节点,可以发现所有与节点 root 路径为奇的节点现在都变成了与节点 reroot 路径为偶的节点,而所有与节点 root 路径为偶的节点现在都变成了与节点 reroot 路径为奇的节点,依次类推,得出结论:对于单独的一个树来说,目标节点的个数是max(x,y)
代码如下:
class Solution {
public int[] maxTargetNodes(int[][] edges1, int[][] edges2) {
List<Integer>[] g1 = createTree(edges1);
List<Integer>[] g2 = createTree(edges2);
int n = edges1.length + 1;
int m = edges2.length + 1;
mx = dfs(0, -1, 0, g2) + 1;
mx = Math.max(mx, m - mx);
int[] ans = new int[n];
ans[0] = dfs(0, -1, 0, g1) + 1 + mx;
dfs1(0, -1, ans, g1);
return ans;
}
int mx;
void dfs1(int x, int fa, int[] ans, List<Integer>[] g){
int n = g.length;
for(int y : g[x]){
if(y != fa){
ans[y] = n - ans[x] + 2*mx;
dfs1(y, x, ans, g);
}
}
}
int dfs(int x, int fa, int k, List<Integer>[] g){
int res = 0;
for(int y : g[x]){
if(y != fa){
res += dfs(y, x, k+1, g) + k%2;
}
}
return res;
}
List<Integer>[] createTree(int[][] edge){
int n = edge.length + 1;
List<Integer>[] g = new ArrayList[n];
Arrays.setAll(g, e -> new ArrayList<>());
for(int[] e : edge){
int x = e[0], y = e[1];
g[x].add(y);
g[y].add(x);
}
return g;
}
}