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

52 阅读4分钟

问题描述

在数据仓库中,表与表之间存在数据依赖关系。例如一张用于电商销售额统计分析表 A ,会依赖商品销售表 B 和商品表 C,商品表 C 会依赖商品类目表 D。如果这些依赖关系中存在循环,那么数据将无法正确地按顺序产出。

目前已知一组数据表的依赖关系,请实现一个函数,用于判断给定的表依赖关系是否能够正常产出数据。

其中给到了以下信息:

参数说明

  • relations: 二维字符串数组,表示表之间的依赖关系
  • 每个子数组的格式为 ["A", "B", "C", ...],其中:
  • 第一个元素 A 表示目标表
  • 后续元素(BC 等)表示 A 所依赖的上游表
  • 子数组的长度至少为 2(即每个表至少依赖一个上游表),但不长于 10

返回值

  • 如果所有表都能够正常产出数据(不存在循环依赖),返回 true;否则返回 false

测试样例

样例1:

输入:relations = [["A", "B", "C"], ["B", "D"], ["C", "E"], ["D", "A"]]
输出:false

样例2:

输入:relations = [["A", "B"], ["B", "C"], ["C", "D"], ["D", "E"]]
输出:true

样例3:

输入:relations = [["X", "Y"], ["Y", "Z"], ["Z", "X"]]
输出:false

题目思路

关于检测有向图中是否存在循环依赖的问题。在数据仓库的上下文中,表之间的依赖关系可以被看作是有向边,表本身是节点。如果存在循环依赖,那么数据的产出顺序将无法确定,因为循环中的任何一个表都无法在其所依赖的表之前产出。

思路的关键在于使用DFS来模拟数据产出的过程,并检测在这个过程中是否会回到已经访问过的节点,从而发现循环依赖。这种方法的时间复杂度通常是O(V+E),其中V是节点的数量,E是边的数量。在这个问题中,由于每个表至少依赖一个上游表,且子数组的长度不长于10,所以算法是可行的。

算法设计

首先明确问题的目标是:给定一组表之间的依赖关系,判断是否存在循环依赖。

输入输出的格式

  • 输入:一个二维字符串数组relations,其中每个子数组表示一个表及其依赖的表。
  • 输出:一个布尔值,表示是否存在循环依赖。

数据结构选择:使用邻接表来表示图,其中键是表名,值是该表依赖的表的列表。这种数据结构适合表示稀疏图,并且便于追踪每个节点的依赖关系。

算法选择:因为深度优先搜索(DFS)算法适合于检测图中的循环,因此选择DFS来遍历图。

代码详解

def solution(relations: list[list[str]]) -> bool:
    # 构建依赖关系图
    graph = {}
    for relation in relations:
        target = relation[0]
        dependencies = relation[1:]
        if target not in graph:
            graph[target] = []
        graph[target].extend(dependencies)
    
    # 记录访问状态的字典
    visited = set()
    visiting = set()
    
    # DFS 函数用于检测循环
    def has_cycle(node):
        if node in visiting:
            return True  # 发现循环
        if node in visited:
            return False  # 已经访问过且无循环
        
        visiting.add(node)
        for neighbor in graph.get(node, []):
            if has_cycle(neighbor):
                return True
        visiting.remove(node)
        visited.add(node)
        return False
    
    # 检查每个节点是否有循环
    for node in graph:
        if has_cycle(node):
            return False
    
    # 检查所有节点是否都被访问过,如果没有,则不存在循环
    for relation in relations:
        for item in relation:
            if item not in visited:
                return False
    
    return True

if __name__ == "__main__":
    # Add your test cases here
    print(solution([["A", "B", "C"], ["B", "D"], ["C", "E"], ["D", "A"]]) == False)
    print(solution([["A", "B"], ["B", "C"], ["C", "D"], ["D", "E"]]) == True)
    print(solution([["X", "Y"], ["Y", "Z"], ["Z", "X"]]) == False)
        

利用MarsCode的智能提示

我在写这道题之前也是使用MarsCode AI询问了一下,给出的框架大致是合理且正确的,在AI给出的代码框架上补充修改,具体如下:

1、构建依赖关系图的逻辑

修改前代码中没有检查target是否已经存在于graph中,这可能会导致重复的依赖关系被覆盖。修改后添加了检查,如果target不在graph中,则初始化一个空列表,然后再添加依赖项。这样可以确保所有依赖关系都被正确记录。

# 修改前的代码
graph[target] = dependencies

# 修改后的代码
if target not in graph:
    graph[target] = []
graph[target].extend(dependencies)

2、检查所有节点是否有循环

修改前的代码只检查了graph中的节点是否有循环。修改后添加了一个额外的循环,检查所有在relations中出现的节点是否都被访问过。如果没有被访问过,意味着存在循环依赖。

# 修改后的代码
for relation in relations:
    for item in relation:
        if item not in visited:
            return False

这些修改使得代码更加健壮,能够正确处理所有节点的循环检查,并且测试用例的输出更加直观。