-
Notifications
You must be signed in to change notification settings - Fork 2
feat(algorithms, graphs, topological-sort): possible recipes from supplies #149
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
3db9b1a
feat(algorithms, graphs, topological-sort): possible recipes from sup…
BrianLusina e5c7cca
updating DIRECTORY.md
35442b6
chore: fix typo
BrianLusina 8315ab1
feat(algorithms, subsets): create subsets package
BrianLusina 90e79aa
feat(algorithms, graphs, topological-sort): alien dictionary
BrianLusina 524c6a1
feat(algorithms, graphs, topoligical-sort): alien dictionary variation 2
BrianLusina 50282e6
updating DIRECTORY.md
96ab97e
docs(algorithms, graphs): fix typo
BrianLusina c1630a1
test(algorithms, graphs): fix test case
BrianLusina 3bc898f
test(algorithms, graphs, topological-sort): utility test to validate …
BrianLusina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| # Find All Possible Recipes from Given Supplies | ||
|
|
||
| You are given information about n different recipes. Each recipe is listed in the array recipes, and its corresponding | ||
| ingredients are provided in the 2D array ingredients. The ith recipe, recipes[i], can be prepared if all the necessary | ||
| ingredients listed in ingredients[i] are available. Some ingredients might need to be created from other recipes, meaning | ||
| ingredients[i] may contain strings that are also in recipes. | ||
|
|
||
| Additionally, you have a string array supplies that contains all the ingredients you initially have, and you have an | ||
| infinite supply of each. | ||
|
|
||
| Return a list of all the recipes you can create. The answer can be returned in any order. | ||
|
|
||
| > Note: It is possible for two recipes to list each other as ingredients. However, if these are the only two recipes | ||
| > provided, the expected output is an empty list. | ||
|
|
||
| ## Constraints | ||
|
|
||
| - `n` == `recipes.length` == `ingredients.length` | ||
| - 1 <= `n` <= 100 | ||
| - 1 ≤ `ingredients[i].length`, `supplies.length` ≤ 100 | ||
| - 1 ≤ `recipes[i].length`, `ingredients[i][j].length`, `supplies[k].length` ≤ 10 | ||
| - `recipes[i]`, `ingredients[i][j]`, and `supplies[k]` consist only of lowercase English letters. | ||
| - All the combined values of `recipes` and `supplies` are unique. | ||
| - Each `ingredients[i]` doesn’t contain any duplicate values. | ||
|
|
||
| ## Examples | ||
|
|
||
|  | ||
|  | ||
|
|
||
| ## Solution | ||
|
|
||
| An optimized approach to solve this problem would be to use the topological sort pattern. We need to understand that the | ||
| preparation of recipes depends on the availability of ingredients. Some ingredients are available initially, while others | ||
| may need to be prepared using other recipes. This dependency between recipes and their ingredients can be represented as | ||
| a directed graph where: | ||
|
|
||
| - Each recipe is a node. | ||
| - There is a directed edge from node A to node B if the preparation of recipe B requires recipe A. | ||
|
|
||
| Topological sort is an algorithm commonly used to order nodes (tasks) in a directed acyclic graph (DAG) such that for | ||
| every directed edge u → v, node u comes before node v in the ordering. In our case, this means that if a recipe A depends | ||
| on recipe B, then B should come before A in the order of recipes we can prepare. | ||
|
|
||
| If there are circular dependencies (like two recipes requiring each other), these will be cycles in the graph, and we | ||
| cannot prepare either recipe. The goal of using topological sort is to determine the order in which recipes can be prepared, | ||
| starting from those that can be made with the initial supplies and progressing through those that depend on previously | ||
| prepared recipes. | ||
|
|
||
| Let’s go through the algorithm to see how we will reach the solution: | ||
|
|
||
| 1. Initialize data structures: | ||
| - `recipe_graph`: This is a dictionary where each key is a recipe and its value is a list of recipes that depend on it. | ||
| - `recipe_indegrees`: This is a dictionary that stores the number of prerequisites for each recipe. | ||
| - `recipes_queue`: This is a queue that processes recipes, which can be immediately made with the initial supplies. | ||
| - `possible_recipes`: This is an array that stores all the recipes that can be created. | ||
| 2. Build the graph and in-degree count by iterating over each ingredient of each recipe: | ||
| - If the ingredient is another recipe, add an edge from that ingredient to the current recipe in `recipe_graph` and | ||
| increase `recipe_indegrees` for the current recipe. | ||
| - If the ingredient is available in `supplies`, skip it since it’s immediately available. | ||
| 3. Add all recipes with an in-degree of 0 (no prerequisites) to `recipes_queue`. | ||
| 4. Process the `recipes_queue` using topological sorting: | ||
| - While `recipes_queue` is not empty: | ||
| - Pop a recipe from the queue. | ||
| - Add it to `possible_recipes` since it can now be made. | ||
| - For each recipe that depends on this recipe, reduce its in-degree by 1. | ||
| - If any recipe’s in-degree becomes 0, add it to `recipes_queue`. | ||
| 5. Return the `possible_recipes` list, which contains all the recipes that can be created with the given supplies. | ||
|
|
||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|  | ||
|
|
||
| ### Time Complexity | ||
|
|
||
| The time complexity of this solution is O(v+e), where v is the vertices of the recipe_graph and e is the edges of the | ||
| graph. This is because we traverse each vertex and edge in the graph exactly once during the topological sort, and the | ||
| dictionary data structure allows us to access vertices and edges in constant time. | ||
|
|
||
| ### Space Complexity | ||
|
|
||
| The space complexity is also O(v+e) as we have to store all the recipes and their respective ingredients. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| from typing import List, DefaultDict | ||
| from collections import defaultdict, deque | ||
|
|
||
|
|
||
| def find_recipes( | ||
| recipes: List[str], ingredients: List[List[str]], supplies: List[str] | ||
| ) -> List[str]: | ||
| # Create a graph to store which recipes depend on which ingredients | ||
| recipe_graph: DefaultDict[str, List[str]] = defaultdict(list) | ||
| # Create a dictionary to keep track of the number of dependencies (indegree) for each recipe | ||
| recipe_in_degrees: DefaultDict[str, int] = defaultdict(int) | ||
|
|
||
| # Initialize a queue with the available supplies | ||
| recipe_queue = deque(supplies) | ||
|
|
||
| # Build the graph and indegree dictionary | ||
| for idx, recipe in enumerate(recipes): | ||
| recipe_ingredients = ingredients[idx] | ||
|
|
||
| for ingredient in recipe_ingredients: | ||
| recipe_in_degrees[recipe] += 1 | ||
| recipe_graph[ingredient].append(recipe) | ||
|
|
||
| # List to store the possible recipes that can be made | ||
| possible_recipies: List[str] = [] | ||
|
|
||
| # Process the queue until it's empty | ||
| while recipe_queue: | ||
| # Get the next item (ingredient or supply) from the queue | ||
| ingredient = recipe_queue.popleft() | ||
|
|
||
| # Reduce the indegree of all recipes that depend on the current item | ||
| for recipe in recipe_graph[ingredient]: | ||
| recipe_in_degrees[recipe] -= 1 | ||
| # If a recipe's indegree reaches 0, all its ingredients are available, so add it to the queue | ||
| if recipe_in_degrees[recipe] == 0: | ||
| possible_recipies.append(recipe) | ||
| recipe_queue.append(recipe) | ||
|
|
||
| return possible_recipies | ||
Binary file added
BIN
+63.2 KB
...cipes_supplies/images/examples/all_possible_recipes_from_supplies_example_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+74.1 KB
...cipes_supplies/images/examples/all_possible_recipes_from_supplies_example_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+144 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+154 KB
...es_supplies/images/solutions/all_possible_recipes_from_supplies_solution_10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+162 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+159 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+176 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+150 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_5.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+178 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_6.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+154 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_7.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+182 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+155 KB
...pes_supplies/images/solutions/all_possible_recipes_from_supplies_solution_9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
69 changes: 69 additions & 0 deletions
69
algorithms/graphs/recipes_supplies/test_find_all_possible_recipes.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| import unittest | ||
| from typing import List | ||
| from parameterized import parameterized | ||
| from algorithms.graphs.recipes_supplies import find_recipes | ||
|
|
||
| FIND_ALL_POSSIBLE_RECIPES_TEST_CASES = [ | ||
| ( | ||
| ["tea", "omelette"], | ||
| [["milk", "caffeine", "sugar"], ["salt", "egg", "pepper"]], | ||
| ["salt", "milk", "egg", "caffeine", "sugar"], | ||
| ["tea"], | ||
| ), | ||
| ( | ||
| ["sandwich", "mojito"], | ||
| [["cheese", "vegetables", "bread", "salad"], ["rum", "mint", "syrup"]], | ||
| ["cheese", "rum", "bread", "salad", "vegetables", "mint", "syrup"], | ||
| ["sandwich", "mojito"], | ||
| ), | ||
| ( | ||
| ["bread", "sandwich", "burger"], | ||
| [["yeast", "flour"], ["bread", "meat"], ["sandwich", "meat", "bread"]], | ||
| ["yeast", "flour", "meat"], | ||
| ["bread", "sandwich", "burger"], | ||
| ), | ||
| ( | ||
| ["bread", "sandwich"], | ||
| [["yeast", "flour"], ["bread", "meat"]], | ||
| ["yeast", "flour", "meat"], | ||
| ["bread", "sandwich"], | ||
| ), | ||
| ( | ||
| ["bread"], | ||
| [["yeast", "flour"]], | ||
| ["yeast", "flour", "corn"], | ||
| ["bread"], | ||
| ), | ||
| ( | ||
| ["pasta", "egg", "chicken"], | ||
| [["yeast", "flour"], ["pasta", "meat"], ["egg", "meat", "pasta"]], | ||
| ["yeast", "flour", "meat"], | ||
| ["pasta", "egg", "chicken"], | ||
| ), | ||
| ( | ||
| ["custard", "trifle"], | ||
| [ | ||
| ["yeast", "flour", "trifle", "bananas", "eggs", "milk"], | ||
| ["eggs", "milk", "custard"], | ||
| ], | ||
| ["eggs", "milk", "yeast", "flour", "corn", "bananas"], | ||
| [], | ||
| ), | ||
| ] | ||
|
|
||
|
|
||
| class FindAllPossibleRecipesTestCase(unittest.TestCase): | ||
| @parameterized.expand(FIND_ALL_POSSIBLE_RECIPES_TEST_CASES) | ||
| def test_find_recipes( | ||
| self, | ||
| recipies: List[str], | ||
| ingredients: List[List[str]], | ||
| supplies: List[str], | ||
| expected: List[str], | ||
| ): | ||
| actual = find_recipes(recipies, ingredients, supplies) | ||
| self.assertEqual(expected, actual) | ||
BrianLusina marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| unittest.main() | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.