- 收集树中金币
提示
困难
139
相关企业
给你一个 n 个节点的无向无根树,节点编号从 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges ,其中 edges[i] = [ai, bi] 表示树中节点 ai 和 bi 之间有一条边。再给你一个长度为 n 的数组 coins ,其中 coins[i] 可能为 0 也可能为 1 ,1 表示节点 i 处有一个金币。
一开始,你需要选择树中任意一个节点出发。你可以执行下述操作任意次:
收集距离当前节点距离为 2 以内的所有金币,或者
移动到树中一个相邻节点。
你需要收集树中所有的金币,并且回到出发节点,请你返回最少经过的边数。
如果你多次经过一条边,每一次经过都会给答案加一。
示例 1:
输入:coins = [1,0,0,0,0,1], edges = [[0,1],[1,2],[2,3],[3,4],[4,5]]
输出:2
解释:从节点 2 出发,收集节点 0 处的金币,移动到节点 3 ,收集节点 5 处的金币,然后移动回节点 2 。
示例 2:
输入:coins = [0,0,0,1,1,0,0,1], edges = [[0,1],[0,2],[1,3],[1,4],[2,5],[5,6],[5,7]]
输出:2
解释:从节点 0 出发,收集节点 4 和 3 处的金币,移动到节点 2 处,收集节点 7 处的金币,移动回节点 0 。
提示:
n == coins.length
1 <= n <= 3 * 104
0 <= coins[i] <= 1
edges.length == n - 1
edges[i].length == 2
0 <= ai, bi < n
ai != bi
edges 表示一棵合法的树。
通过次数
13.6K
提交次数
22.9K
通过率
59.4%
解题思路
根据题意,节点示意图为无法确定根的二叉树模型,即只有叶子节点的度为1
根据题意,可将所有度为1且无金币存在的叶子节点删除,二叉树内所有叶子节点的都存在金币为止
将得到恶的二叉树再次进行裁剪,在确保裁剪后所有金币仍都在树上的前提下(此次裁剪荏仍然遵循度只裁剪度等于1的边),每个叶子节点才裁减到爷爷节点位置
统计二叉树内便边的条数,乘2后为最终结果。
解题思路 + 代码
class Solution {
public int collectTheCoins(int[] coins, int[][] edges) {
int n_len = coins.length;
int ans = n_len - 1;
List<Integer>[] list_count = new List[n_len];
List<Integer> list_coins = new ArrayList<>();
int[] degree = new int[n_len];
for (int i = 0; i < n_len; i++) list_count[i] = new ArrayList<>();
for (int i = 0; i < n_len - 1; i++) {
int col = edges[i][0];
int row = edges[i][1];
list_coins.add(coins[i]);
list_count[col].add(row);
list_count[row].add(col);
++degree[col];
++degree[row];
}
boolean[] list_ele_flag = new boolean[n_len];
if (!list_coins.contains(1)) return 0;
if (!list_coins.contains(0)) {
} else {
int index_ele = 0;
Arrays.fill(list_ele_flag, true);
while (index_ele < n_len) {
int index = index_ele;
int index_1;
while (list_ele_flag[index] && coins[index] == 0 && degree[index] == 1) {
index_1 = list_count[index].get(0);
list_count[index].remove(0);
list_count[index_1].remove((Object) index);
--degree[index];
--degree[index_1];
--ans;
list_ele_flag[index] = false;
index = index_1;
}
do {
index_ele++;
} while (!list_ele_flag[index++] && index < n_len);
}
}
Arrays.fill(list_ele_flag, true);
for (int j = 0; j < n_len; j++) {
if (degree[j] == 1 && list_ele_flag[j]) {
int index_1 = list_count[j].get(0); //与该节点连接的节点
list_count[j].remove(0);
list_count[index_1].remove((Object) j); //删除list_count中该链接的记录
--degree[j];
--degree[index_1]; //删除因此链接产生的度
--ans; // 删除一条边
list_ele_flag[index_1] = false;
}
}
for (int j = 0; j < n_len; j++) {
if (degree[j] == 1) --ans; // 删除一条边
}
return ans <= 0 ? 0 : ans * 2;
}
}
具体分析其时间复杂度
代码中包含四个n_len次数的循环和一个嵌套的while循环,但是while循环中循环体的执行次数实际上为n_len次,循环体中的第二个while循环仅仅为了缩减主循环体的判断次数;所以的最终时间复杂度为n, 但是实际执行过程中,因为循环体中存在list数组删除具体某个元素的操作,所以导致耗时较长