问题描述
在数据仓库中,表与表之间存在数据依赖关系。例如一张用于电商销售额统计分析表 A ,会依赖商品销售表 B 和商品表 C,商品表 C 会依赖商品类目表 D。如果这些依赖关系中存在循环,那么数据将无法正确地按顺序产出。
目前已知一组数据表的依赖关系,请实现一个函数,用于判断给定的表依赖关系是否能够正常产出数据。
其中给到了以下信息:
参数说明
relations: 二维字符串数组,表示表之间的依赖关系- 每个子数组的格式为
["A", "B", "C", ...],其中: - 第一个元素
A表示目标表 - 后续元素(
B、C等)表示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
这些修改使得代码更加健壮,能够正确处理所有节点的循环检查,并且测试用例的输出更加直观。