java题解"数据表是否能顺利产出" | 豆包MarsCode AI刷题

123 阅读4分钟

问题描述

在离线数据仓库中,某些表的数据产出会依赖更上游的表。例如,一张用于电商销售额统计分析表,会依赖商品销售表和商品表,而商品表又会依赖商品类目表。在数据表的依赖关系中,如果存在循环依赖,数据将无法顺利产出。

给定一组数据表的依赖关系,判断数据是否能顺利产出。

输入格式

一个数据表的依赖关系列表,每一行表示一个依赖关系,如:A,B,C 表示 A 表数据产出会依赖 B 表和 C 表。以下是一组依赖关系举例,该依赖关系中 A 依赖 BB 依赖 DD 又依赖 A,形成了循环依赖:

A,B,C
B,D
C,E
D,A

输出格式

数据依赖关系正常能产出数据,返回 true,否则返回 false

输入样例

A,B,C
B,D
C,E
D,A

输出样例

false

解题思路

要判断数据表的依赖关系是否存在循环依赖,可以使用图的拓扑排序算法。如果图中存在环(即循环依赖),则无法进行拓扑排序,反之则可以。

具体步骤

  1. 构建图

    • 使用一个哈希表(Map<String, List<String>>)来存储每个表的依赖关系。
    • 使用一个入度表(Map<String, Integer>)来记录每个表的入度(即有多少表依赖于它)。
  2. 初始化入度表

    • 遍历所有依赖关系,增加每个表的入度。
    • 确保所有表都在入度表中,即使它们没有依赖关系。
  3. 拓扑排序

    • 使用一个队列来存储所有入度为0的表。
    • 从队列中取出一个表,将其所有依赖表的入度减1,如果某个依赖表的入度变为0,则将其加入队列。
    • 记录已经处理过的表的数量。
  4. 检查结果

    • 如果所有表都被处理过,说明不存在循环依赖,返回 true
    • 否则,存在循环依赖,返回 false

代码实现

import java.util.*;

public class Main {
    public static boolean solution(String[][] relations) {
        // 构建图
        Map<String, List<String>> graph = new HashMap<>();
        Map<String, Integer> inDegree = new HashMap<>();

        // 初始化图和入度表
        for (String[] relation : relations) {
            String source = relation[0];
            for (int i = 1; i < relation.length; i++) {
                String target = relation[i];
                graph.computeIfAbsent(source, k -> new ArrayList<>()).add(target);
                inDegree.put(target, inDegree.getOrDefault(target, 0) + 1);
            }
            inDegree.putIfAbsent(source, 0); // 确保所有表都在入度表中
        }

        // 初始化队列,存储所有入度为0的表
        Queue<String> queue = new LinkedList<>();
        for (Map.Entry<String, Integer> entry : inDegree.entrySet()) {
            if (entry.getValue() == 0) {
                queue.offer(entry.getKey());
            }
        }

        // 拓扑排序
        int processed = 0;
        while (!queue.isEmpty()) {
            String table = queue.poll();
            processed++;
            if (graph.containsKey(table)) {
                for (String dependent : graph.get(table)) {
                    inDegree.put(dependent, inDegree.get(dependent) - 1);
                    if (inDegree.get(dependent) == 0) {
                        queue.offer(dependent);
                    }
                }
            }
        }

        // 检查是否所有表都被处理过
        return processed == inDegree.size();
    }

    public static void main(String[] args) {
        // 测试用例
        System.out.println(solution(new String[][] {{"A", "B", "C"}, {"B", "D"}, {"C", "E"}, {"D", "A"}}) == false);
        System.out.println(solution(new String[][] {
            {"A", "B", "C", "D", "E"},
            {"F", "G", "H", "I"},
            {"J", "K", "L", "M", "A"},
            {"N", "O", "P", "Q"},
            {"E", "H", "I", "J"},
            {"R", "S", "T", "U"},
            {"V", "W", "X"},
            {"Y", "Z"}}) == false);
    }
}

代码解释

  1. 构建图

    • graph 是一个哈希表,键是表名,值是一个列表,存储该表依赖的所有表。
    • inDegree 是一个哈希表,键是表名,值是该表的入度。
  2. 初始化入度表

    • 遍历所有依赖关系,增加每个表的入度。
    • 确保所有表都在入度表中,即使它们没有依赖关系。
  3. 拓扑排序

    • 使用队列存储所有入度为0的表。
    • 从队列中取出一个表,将其所有依赖表的入度减1,如果某个依赖表的入度变为0,则将其加入队列。
    • 记录已经处理过的表的数量。
  4. 检查结果

    • 如果所有表都被处理过,说明不存在循环依赖,返回 true
    • 否则,存在循环依赖,返回 false

测试用例

  1. 测试用例1

    • 输入:
      A,B,C
      B,D
      C,E
      D,A
      
    • 输出:
      false
      
    • 解释:存在循环依赖 A -> B -> D -> A
  2. 测试用例2

    • 输入:
      A,B,C,D,E
      F,G,H,I
      J,K,L,M,A
      N,O,P,Q
      E,H,I,J
      R,S,T,U
      V,W,X
      Y,Z
      
    • 输出:
      false
      
    • 解释:存在循环依赖 A -> B -> C -> D -> E -> H -> I -> J -> K -> L -> M -> A

通过这种方法,我们可以有效地检测数据表的依赖关系是否存在循环依赖,从而确保数据能够顺利产出。