您吃了吗?---拓扑排序实战:如何用 BFS 解决配方依赖问题?

拓扑排序实战:如何用 BFS 解决配方依赖问题?

在算法面试中,拓扑排序(Topological Sorting) 是一个高频考点,常见于 任务调度、依赖管理、编译顺序、配方制作 等问题。这篇文章将通过 食谱制作问题 ,带你掌握如何利用 BFS 进行拓扑排序

1. 题目背景:配方制作问题

假设你有一些配方(recipes) ,每个配方都需要一定的原料(ingredients)才能制作。同时,你还拥有一些初始原料(supplies)

目标是找出所有可以最终制作出来的配方

示例:

ini 复制代码
String[] recipes = {"bread", "sandwich", "burger"};
List<List<String>> ingredients = List.of(
    List.of("flour", "water"), 
    List.of("bread", "ham"), 
    List.of("sandwich", "lettuce")
);
String[] supplies = {"flour", "water", "ham", "lettuce"};
  • bread 需要 flourwater,可以直接制作。
  • sandwich 需要 breadhambread 可以制作,因此 sandwich 也能制作。
  • burger 需要 sandwichlettucesandwich 可制作,因此 burger 也能制作。

最终输出:["bread", "sandwich", "burger"]

2. 拓扑排序解决方案

这个问题的核心在于 配方之间的依赖关系 ,可以用 拓扑排序 来解决。

解题思路:

  1. 构建有向图(Graph)

    • 把**食材(原料)**视为节点。
    • 配方依赖于原料,形成有向边。
    • 记录 每个配方的入度(in-degree) ,即它所需的原料个数。
  2. 初始化队列(BFS)

    • 将所有 初始可用的原料(supplies) 加入队列。
    • 遍历队列,找到 入度为 0 的 recipe,表示它的所有原料都已经具备。
  3. 执行拓扑排序

    • 不断取出队列中的元素,减少依赖它的 recipe 的入度。
    • 当某个 recipe 的入度变为 0,说明它可以制作,加入队列。

代码实现

ini 复制代码
import java.util.*;

public class RecipeMaker {
    public List<String> findAllRecipes(String[] recipes, List<List<String>> ingredients, String[] supplies) {
        Map<String, List<Integer>> graph = buildGraph(recipes, ingredients);
        Map<String, Integer> inDegree = new HashMap<>();
        Deque<String> queue = new ArrayDeque<>();
        List<String> result = new ArrayList<>();

        for (String supply : supplies) {
            queue.offer(supply);
        }

        while (!queue.isEmpty()) {
            String curIngredient = queue.poll();
            if (!graph.containsKey(curIngredient)) continue;

            for (int recipeIndex : graph.get(curIngredient)) {
                inDegree.put(recipes[recipeIndex], inDegree.get(recipes[recipeIndex]) - 1);
                if (inDegree.get(recipes[recipeIndex]) == 0) {
                    result.add(recipes[recipeIndex]);
                    queue.offer(recipes[recipeIndex]);
                }
            }
        }
        return result;
    }

    private Map<String, List<Integer>> buildGraph(String[] recipes, List<List<String>> ingredients) {
        Map<String, List<Integer>> graph = new HashMap<>();
        Map<String, Integer> inDegree = new HashMap<>();

        for (int recipeIndex = 0; recipeIndex < recipes.length; recipeIndex++) {
            inDegree.put(recipes[recipeIndex], ingredients.get(recipeIndex).size());
            for (String ingredient : ingredients.get(recipeIndex)) {
                graph.putIfAbsent(ingredient, new ArrayList<>());
                graph.get(ingredient).add(recipeIndex);
            }
        }
        return graph;
    }
}

3. 关键规律总结

🟢 规律 1:拓扑排序适用于依赖关系

  • 若 A 依赖 B,则 B 先制作。
  • 适用于 任务调度、编译顺序、配方制作、课程安排等问题

🟢 规律 2:入度(in-degree)控制 recipe 何时可制作

  • 入度 0 代表该 recipe 所有原料可用
  • 逐步减少依赖项,保证 recipe 有序解锁

🟢 规律 3:BFS 逐步解锁更多可制作的 recipe

  • 先制作 直接可用的原料
  • 再解锁 依赖这些原料的 recipe

4. 易错点分析

1. 没有正确初始化 inDegree

  • 需要为每个 recipe 初始化原料数量

2. 漏掉 queue.offer(recipe)

  • 当 recipe 入度变 0 时,必须入队列,否则 BFS 不会继续。

3. 误解 graph 结构

  • graph 存储的是 原料 -> 依赖它的 recipe,而不是 recipe 本身。

5. 经典测试用例

基础用例:所有 recipe 可制作
ini 复制代码
String[] recipes = {"bread", "sandwich"};
List<List<String>> ingredients = List.of(
    List.of("flour", "water"),
    List.of("bread", "ham")
);
String[] supplies = {"flour", "water", "ham"};

输出: ["bread", "sandwich"]

无法制作的 recipe
ini 复制代码
String[] recipes = {"cake"};
List<List<String>> ingredients = List.of(List.of("flour", "sugar", "egg"));
String[] supplies = {"flour", "sugar"};

输出: [] (缺少 egg,无法制作 cake

环形依赖(不可制作)
ini 复制代码
String[] recipes = {"cake", "muffin"};
List<List<String>> ingredients = List.of(
    List.of("muffin"),
    List.of("cake")
);
String[] supplies = {"flour"};

输出: []cake 依赖 muffinmuffin 依赖 cake,形成环)

6. 结语

拓扑排序是一个非常实用的算法技巧,掌握它可以解决大量 任务调度、依赖关系 相关的问题。希望这篇文章能帮你更深入理解 BFS 在拓扑排序中的应用!🚀

相关推荐
QZQ5418832 分钟前
MIT6.824(2024春)Raft-lab3C代码分析
算法
NAGNIP1 小时前
什么是RAG?一文搞懂检索增强生成技术
算法
uhakadotcom1 小时前
王者荣耀排位算法:Elo 排名算法简介
算法·面试·github
Jo乔戈里1 小时前
ass字幕嵌入mp4带偏移
前端·python·算法
机器学习之心1 小时前
基于云模型与TOPSIS评价算法的综合应用研究
算法
鲸临城下2 小时前
R-CNN 模型算法流程梳理
人工智能·深度学习·算法·目标检测·目标跟踪·cnn·r-cnn
GG不是gg2 小时前
矩阵快速幂算法快速上手
算法·矩阵
羑悻的小杀马特2 小时前
K 值选对,准确率翻倍:KNN 算法调参的黄金法则
算法·机器学习
极光雨雨2 小时前
【算法】动态规划
算法·动态规划
开压路机2 小时前
算法:滑动窗口
数据结构·算法