LC 1766. 互质树
题目描述
这是 LeetCode 上的 1766. 互质树 ,难度为 困难。
给你一个 n 个节点的树(也就是一个无环连通无向图),节点编号从 0 到 n - 1,且恰好有 n - 1 条边,每个节点有一个值,树的根节点为 0 号点。
给你一个整数数组 nums 和一个二维数组 edges 来表示这棵树。
nums[i] 表示第 i 个点的值,$edges[j] = [u{j}, v{j}]$ 表示节点 $u{j}$ 和节点 $v{j}$ 在树中有一条边。
当 gcd(x, y) == 1,我们称两个数 x 和 y 是 互质的 ,其中 gcd(x, y) 是 x 和 y 的最大公约数。
从节点 i 到根最短路径上的点都是节点 i 的祖先节点,一个节点不是它自己的祖先节点。
请你返回一个大小为 n 的数组 ans,其中 ans[i] 是离节点 i 最近的祖先节点且满足 nums[i] 和 nums[ans[i]] 是互质的,如果不存在这样的祖先节点,ans[i] 为 -1。
示例 1:

| 1 |  | 
示例 2:

| 1 |  | 
提示:
- $nums.length = n$
- $1 <= nums[i] <= 50$
- $1 <= n <= 10^5$
- $edges.length = n - 1$
- $edges[j].length = 2$
- $0 <= u{j}, v{j} < n$
- $u{j} \neq v{j}$
DFS
题目描述很长,但其实就是说每个节点从下往上找,找到最近的「与其互质」的节点。
数据范围是 $10^5$,如果每个节点都直接往上找最近「互质」祖宗节点的话,当树为线性时,复杂度是 $O(n^2)$ ,会超时。
因此我们要利用 $nums[i]$ 范围只有 $50$ 的特性。
我们可以先预处理除 $[1, 50]$ 范围内的每个数,求出他们互质的数有哪些,存到一个字典里。
那么对于某个节点而言,假设节点的值为 x ,所在层数为 y。
那么问题转化为求与 x 互质的数有哪些,最近的在哪一层。
用 dep[x] 表示距离值为 x 的节点最近的层是多少;pos[x] 代表具体的节点编号。
代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58class Solution {
    int[] ans;
    Map<Integer, List<Integer>> map = new HashMap<>(); // 边映射
    Map<Integer, List<Integer>> val = new HashMap<>(); // 互质数字典
    int[] dep;
    int[] pos = new int[52];
    public int[] getCoprimes(int[] nums, int[][] edges) {
        int n = nums.length;
        ans = new int[n];
        dep = new int[n];
        Arrays.fill(ans, - 1);
        Arrays.fill(pos, -1);
        for (int[] edge : edges) {
            int a = edge[0], b = edge[1];
            List<Integer> alist = map.getOrDefault(a, new ArrayList<>());
            alist.add(b);
            map.put(a, alist);
            List<Integer> blist = map.getOrDefault(b, new ArrayList<>());
            blist.add(a);
            map.put(b, blist);
        }
        for (int i = 1; i <= 50; i++) {
            for (int j = 1; j <= 50; j++) {
                if (gcd(i, j) == 1) {
                    List<Integer> list = val.getOrDefault(i, new ArrayList<>());
                    list.add(j);
                    val.put(i, list);
                }
            }
        }
        dfs(nums, 0, -1);
        return ans;
    }
    void dfs(int[] nums, int u, int form) {
        int t = nums[u];
        for (int v : val.get(t)) {
            if (pos[v] == -1) continue;
            if (ans[u] == -1 || dep[ans[u]] < dep[pos[v]]) ans[u] = pos[v];
        }
        int p = pos[t];
        pos[t] = u;
        for (int i : map.get(u)) {
            if (i == form) continue;
            dep[i] = dep[u] + 1;
            dfs(nums, i, u);
        }
        pos[t] = p;
    }
    int gcd(int a, int b) {
        if (b == 0) return a;
        if (a == 0) return b;
        return gcd(b, a % b);
    }
}
- 时间复杂度:对于每个节点而言,会检查与其数值互质的数有哪些,在哪层。最坏情况下会检查 50 个互质数(当前数值为 1)。复杂度为 $O(n)$
- 空间复杂度:$O(n)$
最后
这是我们「刷穿 LeetCode」系列文章的第 No.1766 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。
在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。
为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:https://github.com/SharingSource/LogicStack-LeetCode 。
在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!
