Loading problems...
TL;DR: Model the recipes and ingredients as a directed graph and perform a Topological Sort (Kahn's Algorithm) to determine which recipes can be completed starting from the initial supplies.
The problem asks us to determine which recipes can be created given a set of initial supplies and a list of dependencies (ingredients) for each recipe. A crucial detail is that a created recipe can subsequently be used as an ingredient for another recipe. This creates a chain of dependencies. We need to return all recipes that can eventually be created.
This is a classic dependency resolution problem, making "LeetCode 2115" a standard application of graph theory concepts, specifically designed to test understanding of directed acyclic graphs (DAGs) and order of execution.
The naive approach attempts to simulate the creation process iteratively. We can maintain a set of currently available items (initially just the supplies). In each iteration, we loop through every recipe that hasn't been created yet. For each recipe, we check if all its required ingredients exist in our available set. If they do, we mark the recipe as created, add it to the available set, and repeat the process.
We continue these iterations until a full pass over the recipes results in no new creations.
python
# Pseudo-code for Brute Force
available = set(supplies)
created_recipes = []
changed = True
while changed:
changed = False
for i in range(len(recipes)):
if recipe[i] not in available:
if all(ingredient in available for ingredient in ingredients[i]):
available.add(recipe[i])
created_recipes.append(recipe[i])
changed = TrueWhy this approach is suboptimal: The time complexity is roughly , where is the number of recipes and is the total number of ingredients across all recipes. In the worst-case scenario (a long chain where Recipe A allows Recipe B, which allows Recipe C, etc.), we might iterate through the list of recipes times. For large inputs, this redundant checking leads to Time Limit Exceeded (TLE) or poor performance compared to linear solutions.
The key intuition is to view ingredients and recipes as nodes in a graph. The relationship "Recipe A requires Ingredient B" can be modeled as a directed edge from B to A (). This direction signifies that having B contributes to unlocking A.
Using this model, the problem transforms into finding all nodes that can be reached and "processed" starting from the initial set of zero-dependency nodes (the supplies).
Key Mapping to Kahn's Algorithm:
supplies.By processing the supplies, we "unlock" dependencies. Every time we process an item, we look at the recipes that need it and decrement their in-degree count. If a recipe's in-degree drops to 0, it means all its ingredients are available. We then add this recipe to the queue, allowing it to unlock further recipes.
This approach efficiently handles cascades of recipes (e.g., Bread Sandwich Burger) and naturally handles cycles (e.g., A needs B, B needs A) by simply never reducing their in-degrees to zero.

We will implement Kahn's Algorithm to solve this problem efficiently.
Graph Construction:
graph[item] contains a list of recipes that require item.in_degree map where in_degree[recipe] stores the count of ingredients needed for that recipe.Initialization:
graph and in_degree map by iterating through the recipes and ingredients input arrays.std::queue in C++) with the initial supplies. These are our starting nodes with effectively "zero unmet dependencies."BFS Traversal:
graph[current_item] (recipes that need this item).in_degree of each neighbor.in_degree becomes 0, it means the recipe is now possible. Add it to the queue and to our result list.Output:
Let's trace the algorithm with a simple example:
recipes = ["bread", "sandwich"], ingredients = [["flour"], ["bread", "meat"]], supplies = ["flour", "meat"].
Build Graph & In-Degrees:
in_degree: {"bread": 1, "sandwich": 2}graph: {"flour": ["bread"], "bread": ["sandwich"], "meat": ["sandwich"]}Initialize Queue:
["flour", "meat"] (from supplies).Process "flour":
["bread"].in_degree["bread"] from 1 to 0.in_degree["bread"] is 0. Add "bread" to Queue and Result.["meat", "bread"]. Result: ["bread"].Process "meat":
["sandwich"].in_degree["sandwich"] from 2 to 1.["bread"].Process "bread":
["sandwich"].in_degree["sandwich"] from 1 to 0.in_degree["sandwich"] is 0. Add "sandwich" to Queue and Result.["sandwich"]. Result: ["bread", "sandwich"].Process "sandwich":
Final Output: ["bread", "sandwich"].
The algorithm is correct based on the invariant of Topological Sort. A node (recipe) is only added to the queue when its in-degree reaches zero. The in-degree represents the count of unsatisfied dependencies.
cpp#include <vector> #include <string> #include <unordered_map> #include <unordered_set> #include <queue> using namespace std; class Solution { public: vector<string> findAllRecipes(vector<string>& recipes, vector<vector<string>>& ingredients, vector<string>& supplies) { // Graph: ingredient -> list of recipes that need it unordered_map<string, vector<string>> graph; // In-degree: recipe -> count of missing ingredients unordered_map<string, int> in_degree; // Initialize in-degrees for all recipes for (const string& recipe : recipes) { in_degree[recipe] = 0; } // Build the graph for (int i = 0; i < recipes.size(); ++i) { string recipe = recipes[i]; for (const string& ingredient : ingredients[i]) { graph[ingredient].push_back(recipe); in_degree[recipe]++; } } // Queue for Kahn's Algorithm, initialized with supplies queue<string> q; for (const string& supply : supplies) { q.push(supply); } vector<string> result; while (!q.empty()) { string current = q.front(); q.pop(); // If current item is a recipe (and not just a raw supply), add to result // Note: We can check if it was in the original recipes list, // or simply rely on the flow since supplies are not recipes. // However, the problem asks for recipes specifically. // A simple check is if it's in our in_degree map. if (in_degree.find(current) != in_degree.end()) { result.push_back(current); } // Unlock neighbors if (graph.find(current) != graph.end()) { for (const string& neighbor : graph[current]) { in_degree[neighbor]--; if (in_degree[neighbor] == 0) { q.push(neighbor); } } } } return result; } };
javaimport java.util.*; class Solution { public List<String> findAllRecipes(String[] recipes, List<List<String>> ingredients, String[] supplies) { // Graph: ingredient -> list of recipes dependent on it Map<String, List<String>> graph = new HashMap<>(); // In-degree: recipe -> number of ingredients needed Map<String, Integer> inDegree = new HashMap<>(); // Initialize in-degree for all recipes for (String recipe : recipes) { inDegree.put(recipe, 0); } // Build graph and calculate in-degrees for (int i = 0; i < recipes.length; i++) { String recipe = recipes[i]; List<String> neededIngredients = ingredients.get(i); for (String ingredient : neededIngredients) { graph.putIfAbsent(ingredient, new ArrayList<>()); graph.get(ingredient).add(recipe); inDegree.put(recipe, inDegree.get(recipe) + 1); } } // Queue for BFS, initialized with supplies Queue<String> queue = new LinkedList<>(); for (String supply : supplies) { queue.offer(supply); } List<String> result = new ArrayList<>(); while (!queue.isEmpty()) { String current = queue.poll(); // If the graph contains dependencies for this item if (graph.containsKey(current)) { for (String neighbor : graph.get(current)) { inDegree.put(neighbor, inDegree.get(neighbor) - 1); if (inDegree.get(neighbor) == 0) { queue.offer(neighbor); result.add(neighbor); } } } } return result; } }
pythonfrom collections import deque, defaultdict from typing import List class Solution: def findAllRecipes(self, recipes: List[str], ingredients: List[List[str]], supplies: List[str]) -> List[str]: # Graph: ingredient -> list of recipes that need it graph = defaultdict(list) # In-degree: recipe -> count of missing ingredients in_degree = {recipe: 0 for recipe in recipes} # Build graph for recipe, ing_list in zip(recipes, ingredients): for ing in ing_list: graph[ing].append(recipe) in_degree[recipe] += 1 # Initialize queue with supplies queue = deque(supplies) result = [] while queue: current = queue.popleft() # If current item unlocks any recipes if current in graph: for neighbor in graph[current]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: queue.append(neighbor) result.append(neighbor) return result
Let be the total number of unique ingredients and recipes, and be the total number of dependencies (sum of the lengths of all ingredient lists).
The Graph BFS - Topological Sort pattern is versatile and appears in many "dependency resolution" problems.
Why does my solution fail on cycles?
RecursionError or Time Limit Exceeded. Kahn's algorithm handles this naturally (in-degrees never hit 0).Why is my graph traversal producing the wrong order or results?
Why am I getting TLE with a simulation approach?
recipes is large, repeated scanning is inefficient.Why are supplies not unlocking recipes?
supplies to the queue.