
在处理复杂数据结构时,我们常会遇到需要从一个具有层级或图状关系的字典中,根据特定规则提取信息的情况。假设我们有一个表示有向图的字典my_dict,其中键是节点,值是其直接邻居节点列表。我们还定义了一个source_list作为起始节点集,以及一个target_list作为终止节点集。我们的目标是,从source_list中的每个节点开始,逐层遍历my_dict,直到遇到target_list中的任一节点为止。同时,我们希望将每次遍历层级(迭代)所发现的节点及其邻居组织成一个字典,最终输出一个以迭代次数为键的嵌套字典。
例如,给定以下数据:
source_list = ['a', 'b']
target_list = ['x', 'y', 'z']
my_dict = {
'a': ['e'],
'b': ['f', 'd'],
'e': ['g'],
'f': ['t', 'h'],
'd': ['x'],
'g': ['x'],
't': ['y'],
'h': ['z']
}期望的输出是:
{0: {'a': ['e'], 'b': ['f', 'd']},
1: {'e': ['g'], 'f': ['t', 'h'], 'd': ['x']},
2: {'g': ['x'], 't': ['y'], 'h': ['z']}}这里,键0代表第一层迭代,包含从source_list直接可达的节点及其邻居;键1代表第二层迭代,包含从第一层节点可达的节点及其邻居,以此类推。
最初尝试的解决方案可能使用简单的循环结构,但往往难以正确地管理层级关系并按期望的迭代次数组织输出。这种按层级(或深度)遍历数据结构的需求,正是广度优先搜索(BFS)算法的典型应用场景。
立即学习“Python免费学习笔记(深入)”;
BFS是一种用于遍历或搜索树或图的算法。它从图的某个节点开始,首先访问其所有邻居节点,然后访问这些邻居节点的邻居,依此类推。换句话说,它会先访问距离起始节点“最近”的所有节点,然后再访问距离次之的节点,确保了按层级(或迭代)进行探索。这与我们的需求完美契合,因为我们需要精确地记录每一层迭代所发现的节点。
我们将介绍两种基于BFS的实现方式。
此实现使用collections.deque作为队列,以高效地管理待访问节点。它通过在队列中存储(level, node)元组来跟踪当前节点的层级。
from collections import deque
def bfs_fetch_by_level(source_nodes, target_nodes, graph_dict):
"""
使用广度优先搜索从字典中按层级提取数据。
Args:
source_nodes (list): 起始节点列表。
target_nodes (list): 目标节点列表。
graph_dict (dict): 表示图结构的字典,键为节点,值为其邻居列表。
Returns:
dict: 按层级组织的提取结果字典。
"""
queue = deque((0, node) for node in source_nodes) # 队列存储 (层级, 节点)
target_set = set(target_nodes) # 目标节点集合,用于快速查找
seen = set(source_nodes) # 已访问节点集合,防止重复访问和循环
result = {} # 存储最终结果
while queue:
level, current_node = queue.popleft() # 取出当前层级和节点
# 获取当前节点的邻居,如果不存在则为空列表
neighbors = graph_dict.get(current_node, [])
# 将当前节点及其邻居添加到结果字典的对应层级中
result.setdefault(level, {})[current_node] = neighbors[:] # 使用[:]进行浅拷贝,避免修改原始列表
for neighbor in neighbors:
# 如果邻居节点已访问过或在目标列表中,则跳过
# 如果在目标列表中,我们不希望继续探索其子节点,因为已达到目标
if neighbor in seen or neighbor in target_set:
continue
seen.add(neighbor) # 标记为已访问
queue.append((level + 1, neighbor)) # 将邻居加入队列,层级加1
return result
# 示例使用
source_list = ['a', 'b']
target_list = ['x', 'y', 'z']
my_dict = {
'a': ['e'],
'b': ['f', 'd'],
'e': ['g'],
'f': ['t', 'h'],
'd': ['x'],
'g': ['x'],
't': ['y'],
'h': ['z']
}
output_bfs = bfs_fetch_by_level(source_list, target_list, my_dict)
print(output_bfs)输出:
{0: {'a': ['e'], 'b': ['f', 'd']}, 1: {'e': ['g'], 'f': ['t', 'h'], 'd': ['x']}, 2: {'g': ['x'], 't': ['y'], 'h': ['z']}}代码解析:
第二种实现方式在构建每一层结果时略有不同,它通过一个内部循环来确保当前层的所有节点都被处理完毕,然后才递增层级。这种方式可能在某些情况下更清晰地表达层级概念。
from collections import deque
def build_level_dict(graph, queue, seen, target_set):
"""
辅助函数:构建当前层级的字典。
"""
# 记录当前层级的最后一个节点,用于判断何时结束本层处理
current_level_end_node = queue[-1] if queue else None
level_dict = {}
while True:
node = queue.popleft()
neighbors = graph.get(node, [])
level_dict[node] = neighbors[:]
for neighbor in neighbors:
if neighbor in seen or neighbor in target_set:
continue
seen.add(neighbor)
queue.append(neighbor)
if node == current_level_end_node: # 当前层所有节点已处理完毕
return level_dict
def optimized_bfs_fetch_by_level(source_nodes, target_nodes, graph_dict):
"""
优化版广度优先搜索,按层级提取数据。
"""
target_set = set(target_nodes)
result = {}
# 初始已访问节点包含源节点
seen = set(source_nodes)
queue = deque(source_nodes) # 队列只存储节点,层级通过外部循环管理
level = 0
while queue:
# 调用辅助函数构建当前层级的结果
result[level] = build_level_dict(graph_dict, queue, seen, target_set)
level += 1 # 层级递增
return result
# 示例使用
source_list = ['a', 'b']
target_list = ['x', 'y', 'z']
my_dict = {
'a': ['e'],
'b': ['f', 'd'],
'e': ['g'],
'f': ['t', 'h'],
'd': ['x'],
'g': ['x'],
't': ['y'],
'h': ['z']
}
output_optimized_bfs = optimized_bfs_fetch_by_level(source_list, target_list, my_dict)
print(output_optimized_bfs)输出:
{0: {'a': ['e'], 'b': ['f', 'd']}, 1: {'e': ['g'], 'f': ['t', 'h'], 'd': ['x']}, 2: {'g': ['x'], 't': ['y'], 'h': ['z']}}代码解析:
通过这两种基于广度优先搜索的实现,我们能够有效地从复杂的嵌套字典结构中,按照指定的起始节点和目标节点,按层级迭代地提取所需数据,并以清晰的结构化格式呈现。这种方法不仅适用于本例中的特定场景,也广泛应用于各种图遍历和最短路径查找问题。
以上就是使用广度优先搜索(BFS)从Python字典中按层级提取数据的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号