From 5822928b77dbb679d39c4b0d64cb22b70dae9ef5 Mon Sep 17 00:00:00 2001 From: Lane Zhang Date: Mon, 5 May 2025 17:00:05 +0000 Subject: [PATCH] Update all solutions' changes in 2025-05-05. --- .../797-all-paths-from-source-to-target.md | 271 +++++++++++++++++ .../797-all-paths-from-source-to-target.md | 272 ++++++++++++++++++ 2 files changed, 543 insertions(+) diff --git a/en/1-1000/797-all-paths-from-source-to-target.md b/en/1-1000/797-all-paths-from-source-to-target.md index 9ae3115..d9eaf5a 100644 --- a/en/1-1000/797-all-paths-from-source-to-target.md +++ b/en/1-1000/797-all-paths-from-source-to-target.md @@ -354,6 +354,19 @@ Recursion is an important concept in computer science and mathematics, which ref - **Base case**: Equivalent to the termination condition. After reaching the base case, the result can be returned without recursive calls to prevent infinite loops. - **Recursive step**: The step by which the problem gradually approaches the "base case". +## Step by Step Solutions + +1. Initialize an empty list `paths` to store all the valid paths found from source to target. +2. Define a recursive Depth-First Search (DFS) function, say `dfs`, that takes the current `node` and the `currentPath` (a list of nodes visited so far to reach the current node) as input. +3. Inside the `dfs` function: + a. Create a new path list by appending the current `node` to the `currentPath`. Let's call this `newPath`. + b. Check if the current `node` is the target node (`n - 1`, where `n` is the total number of nodes). + i. If it is the target node, it means we've found a complete path. Add `newPath` to the main `paths` list and return from this recursive call. + c. If the current `node` is not the target node, iterate through all `neighbor` nodes accessible from the current `node` (i.e., iterate through `graph[node]`). + i. For each `neighbor`, make a recursive call to `dfs` with the `neighbor` as the new current node and `newPath` as the path taken to reach it (`dfs(neighbor, newPath)`). +4. Start the process by calling the `dfs` function with the source node `0` and an empty initial path (`dfs(0, [])`). +5. After the initial `dfs` call completes, return the `paths` list containing all discovered paths. + ## Complexity - Time complexity: `Too complex`. @@ -586,6 +599,264 @@ end // Welcome to create a PR to complete the code of this language, thanks! ``` +## Intuition 3 + + + +## Pattern of "Depth-First Search" + +**Depth-First Search (DFS)** is a classic graph traversal algorithm characterized by its **"go as deep as possible"** approach when exploring branches of a graph. Starting from the initial vertex, DFS follows a single path as far as possible until it reaches a vertex with no unvisited adjacent nodes, then backtracks to the nearest branching point to continue exploration. This process is implemented using **recursion** or an **explicit stack (iterative method)**, resulting in a **Last-In-First-Out (LIFO)** search order. As a result, DFS can quickly reach deep-level nodes far from the starting point in unweighted graphs. + +**Comparison with BFS**: + +1. **Search Order**: DFS prioritizes depth-wise exploration, while Breadth-First Search (BFS) expands layer by layer, following a **First-In-First-Out (FIFO)** queue structure. +2. **Use Cases**: DFS is better suited for strongly connected components or backtracking problems (e.g., finding all paths in a maze), whereas BFS excels at finding the shortest path (in unweighted graphs) or exploring neighboring nodes (e.g., friend recommendations in social networks). + +**Unique Aspects of DFS**: + +- **Incompleteness**: If the graph is infinitely deep or contains cycles (without visited markers), DFS may fail to terminate, whereas BFS always finds the shortest path (in unweighted graphs). +- **"One-path deep-dive"** search style makes it more flexible for backtracking, pruning, or path recording, but it may also miss near-optimal solutions. + +In summary, DFS reveals the vertical structure of a graph through its depth-first strategy. Its inherent backtracking mechanism, combined with the natural use of a stack, makes it highly effective for path recording and state-space exploration. However, precautions must be taken to handle cycles and the potential absence of optimal solutions. + +## Step by Step Solutions + +1. Initialize an empty list `paths` to store all valid paths found from the source to the target. +2. Create a mutable list `path` to track the current path being explored, initially containing only the source node `0`. +3. Implement a recursive DFS function that explores paths using backtracking: + - Base case: If the current node is the target node (`n-1`), make a copy of the current path and add it to the `paths` list. + - Recursive step: For each neighbor of the current node: + - Add the neighbor to the current path. + - Recursively call the DFS function with this neighbor. + - After the recursive call returns, remove the neighbor from the path (backtrack). +4. Start the DFS from the source node `0`. +5. Return the collected `paths` after the DFS completes. + +## Complexity + +- Time complexity: `Too complex`. +- Space complexity: `Too complex`. + +## Python + +```python +class Solution: + def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: + self.paths = [] + self.graph = graph + self.path = [0] # Important + + self.dfs(0) + + return self.paths + + def dfs(self, node): + if node == len(self.graph) - 1: + self.paths.append(self.path.copy()) # Important + return + + for neighbor in self.graph[node]: + self.path.append(neighbor) # Important + self.dfs(neighbor) + self.path.pop() # Important + +``` + +## Java + +```java +class Solution { + private List> paths = new ArrayList<>(); + private List path = new ArrayList<>(List.of(0)); // Important - start with node 0 + private int[][] graph; + + public List> allPathsSourceTarget(int[][] graph) { + this.graph = graph; + + dfs(0); + + return paths; + } + + private void dfs(int node) { + if (node == graph.length - 1) { // Base case + paths.add(new ArrayList<>(path)); // Important - make a copy + return; + } + + for (int neighbor : graph[node]) { // Recursive step + path.add(neighbor); // Important + dfs(neighbor); + path.remove(path.size() - 1); // Important - backtrack + } + } +} +``` + +## C++ + +```cpp +class Solution { +public: + vector> allPathsSourceTarget(vector>& graph) { + _graph = graph; + _path = {0}; // Important - start with node 0 + + dfs(0); + + return _paths; + } + +private: + vector> _paths; + vector> _graph; + vector _path; + + void dfs(int node) { + if (node == _graph.size() - 1) { // Base case + _paths.push_back(_path); // Important - copy is made automatically + return; + } + + for (int neighbor : _graph[node]) { // Recursive step + _path.push_back(neighbor); // Important + dfs(neighbor); + _path.pop_back(); // Important - backtrack + } + } +}; + +``` + +## JavaScript + +```javascript +/** + * @param {number[][]} graph + * @return {number[][]} + */ +var allPathsSourceTarget = function(graph) { + const paths = []; + const path = [0]; // Important - start with node 0 + + function dfs(node) { + if (node === graph.length - 1) { // Base case + paths.push([...path]); // Important - make a copy + return; + } + + for (const neighbor of graph[node]) { // Recursive step + path.push(neighbor); // Important + dfs(neighbor); + path.pop(); // Important - backtrack + } + } + + dfs(0); + + return paths; +}; +``` + +## C# + +```csharp +public class Solution +{ + private IList> paths = new List>(); + private List path = new List { 0 }; // Important - start with node 0 + private int[][] graph; + + public IList> AllPathsSourceTarget(int[][] graph) + { + this.graph = graph; + + Dfs(0); + + return paths; + } + + private void Dfs(int node) + { + if (node == graph.Length - 1) + { // Base case + paths.Add(new List(path)); // Important - make a copy + return; + } + + foreach (int neighbor in graph[node]) + { // Recursive step + path.Add(neighbor); // Important + Dfs(neighbor); + path.RemoveAt(path.Count - 1); // Important - backtrack + } + } +} +``` + +## Go + +```go +func allPathsSourceTarget(graph [][]int) [][]int { + paths := [][]int{} + path := []int{0} // Important - start with node 0 + + var dfs func(int) + dfs = func(node int) { + if node == len(graph) - 1 { // Base case + // Important - make a deep copy of the path + paths = append(paths, append([]int(nil), path...)) + return + } + + for _, neighbor := range graph[node] { // Recursive step + path = append(path, neighbor) // Important + dfs(neighbor) + path = path[:len(path) - 1] // Important - backtrack + } + } + + dfs(0) + + return paths +} +``` + +## Ruby + +```ruby +# @param {Integer[][]} graph +# @return {Integer[][]} +def all_paths_source_target(graph) + @paths = [] + @graph = graph + @path = [0] # Important - start with node 0 + + dfs(0) + + @paths +end + +def dfs(node) + if node == @graph.length - 1 # Base case + @paths << @path.clone # Important - make a copy + return + end + + @graph[node].each do |neighbor| # Recursive step + @path << neighbor # Important + dfs(neighbor) + @path.pop # Important - backtrack + end +end +``` + +## Other languages + +```java +// Welcome to create a PR to complete the code of this language, thanks! +``` + Dear LeetCoders! For a better LeetCode problem-solving experience, please visit website [LeetCodePython.com](https://leetcodepython.com): Dare to claim the best practices of LeetCode solutions! Will save you a lot of time! Original link: [797. All Paths From Source to Target - LeetCode Python/Java/C++/JS code](https://leetcodepython.com/en/leetcode/797-all-paths-from-source-to-target). diff --git a/zh/1-1000/797-all-paths-from-source-to-target.md b/zh/1-1000/797-all-paths-from-source-to-target.md index dfd1723..44b6d9c 100644 --- a/zh/1-1000/797-all-paths-from-source-to-target.md +++ b/zh/1-1000/797-all-paths-from-source-to-target.md @@ -353,6 +353,19 @@ end - **基线情况**:相当于终止条件。到达基线情况后,就可以返回结果了,不需要再递归调用,防止无限循环。 - **递归步骤**:问题逐步向“基线情况”靠近的步骤。 +## 步骤 + +1. 初始化一个空数组 `paths`,用于存储从源节点到目标节点找到的所有有效路径。 +2. 定义一个递归深度优先搜索 (DFS) 函数,例如 `dfs`,它将当前 `node` 和 `path`(即迄今为止访问过的节点列表,用于到达当前节点)作为输入。 +3. 在 `dfs` 函数内部: + - 通过将当前 `node` 附加到 `path` 来创建一个新的路径列表。我们将其称为 `newPath`。 + - 检查当前 `node` 是否为目标节点(`n - 1`,其中 `n` 是节点总数)。 + - 如果是目标节点,则表示我们找到了一条完整的路径。将 `newPath` 添加到主 `paths` 数组中,并从此递归调用返回。 + - 如果当前 `node` 不是目标节点,则遍历所有可从当前 `node` 访问的 `neighbor` 节点(即遍历 `graph[node]`)。 + - 对于每个 `neighbor`,以 `neighbor` 作为新的当前节点,以 `newPath` 作为到达该节点的路径,对 `dfs` 进行递归调用 (`dfs(neighbor, newPath)`)。 +4. 通过使用源节点 `0` 和空的初始路径 (`dfs(0, [])`) 调用 `dfs` 函数来启动该过程。 +5. 初始 `dfs` 调用完成后,返回包含所有已发现路径的 `paths` 数组。 + ## 复杂度 - 时间复杂度: `Too complex`. @@ -585,6 +598,265 @@ end // Welcome to create a PR to complete the code of this language, thanks! ``` +## 思路 3 + + + +## “深度优先搜索”的模式 + +深度优先搜索(DFS)是一种经典的图遍历算法,其核心特点是**“尽可能深”**地探索图的分支。DFS 从起始顶点出发,沿着一条路径不断深入,直到到达没有未访问邻接点的顶点,然后回溯到最近的分叉点继续探索。这一过程通过**递归**或**显式栈(迭代法)**实现,形成“后进先出”的搜索顺序,因此 DFS 在非加权图中能快速找到离起点较远的深层节点。 + +**与 BFS 的对比**: + +1. **搜索顺序**:DFS 优先探索深度方向,而广度优先搜索(BFS)按层逐层扩展,形成“先进先出”的队列结构。 +2. **适用场景**:DFS 更适合强连通分量或回溯类问题(如迷宫所有路径),而 BFS 擅长最短路径(未加权图)或邻近节点探索(如社交网络好友推荐)。 + +**DFS 的独特性**: + +- **不完全性**:若图无限深或存在环路(未标记已访问),DFS 可能无法终止,而 BFS 总能找到最短路径(未加权图)。 +- **“一支笔走到底”** 的搜索风格,使其在回溯剪枝或记录路径时更具灵活性,但也可能错过近邻最优解。 + + +总之,DFS 以深度优先的策略揭示图的纵向结构,其回溯特性与栈的天然结合,使其在路径记录和状态空间探索中表现突出,但需注意处理环路和最优解缺失的局限性。 + +## 步骤 + +1. 初始化一个空列表 `paths`,用于存储从源节点到目标节点找到的所有有效路径。 +2. 创建一个可变列表 `path`,用于跟踪当前正在探索的路径,初始仅包含源节点 `0`。 +3. 实现一个使用回溯法探索路径的递归 DFS 函数: + - 基线情况:如果当前节点是目标节点 (`n-1`),则复制当前路径并将其添加到 `paths` 列表中。 + - 递归步骤:对于当前节点的每个邻居: + - 将邻居添加到当前路径。 + - 使用此邻居递归调用 DFS 函数。 + - 递归调用返回后,从路径中删除邻居(回溯)。 +4. 从源节点 `0` 开始 DFS。 +5. DFS 完成后,返回收集到的 `paths`。 + +## 复杂度 + +- 时间复杂度: `Too complex`. +- 空间复杂度: `Too complex`. + +## Python + +```python +class Solution: + def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]: + self.paths = [] + self.graph = graph + self.path = [0] # Important + + self.dfs(0) + + return self.paths + + def dfs(self, node): + if node == len(self.graph) - 1: + self.paths.append(self.path.copy()) # Important + return + + for neighbor in self.graph[node]: + self.path.append(neighbor) # Important + self.dfs(neighbor) + self.path.pop() # Important + +``` + +## Java + +```java +class Solution { + private List> paths = new ArrayList<>(); + private List path = new ArrayList<>(List.of(0)); // Important - start with node 0 + private int[][] graph; + + public List> allPathsSourceTarget(int[][] graph) { + this.graph = graph; + + dfs(0); + + return paths; + } + + private void dfs(int node) { + if (node == graph.length - 1) { // Base case + paths.add(new ArrayList<>(path)); // Important - make a copy + return; + } + + for (int neighbor : graph[node]) { // Recursive step + path.add(neighbor); // Important + dfs(neighbor); + path.remove(path.size() - 1); // Important - backtrack + } + } +} +``` + +## C++ + +```cpp +class Solution { +public: + vector> allPathsSourceTarget(vector>& graph) { + _graph = graph; + _path = {0}; // Important - start with node 0 + + dfs(0); + + return _paths; + } + +private: + vector> _paths; + vector> _graph; + vector _path; + + void dfs(int node) { + if (node == _graph.size() - 1) { // Base case + _paths.push_back(_path); // Important - copy is made automatically + return; + } + + for (int neighbor : _graph[node]) { // Recursive step + _path.push_back(neighbor); // Important + dfs(neighbor); + _path.pop_back(); // Important - backtrack + } + } +}; + +``` + +## JavaScript + +```javascript +/** + * @param {number[][]} graph + * @return {number[][]} + */ +var allPathsSourceTarget = function(graph) { + const paths = []; + const path = [0]; // Important - start with node 0 + + function dfs(node) { + if (node === graph.length - 1) { // Base case + paths.push([...path]); // Important - make a copy + return; + } + + for (const neighbor of graph[node]) { // Recursive step + path.push(neighbor); // Important + dfs(neighbor); + path.pop(); // Important - backtrack + } + } + + dfs(0); + + return paths; +}; +``` + +## C# + +```csharp +public class Solution +{ + private IList> paths = new List>(); + private List path = new List { 0 }; // Important - start with node 0 + private int[][] graph; + + public IList> AllPathsSourceTarget(int[][] graph) + { + this.graph = graph; + + Dfs(0); + + return paths; + } + + private void Dfs(int node) + { + if (node == graph.Length - 1) + { // Base case + paths.Add(new List(path)); // Important - make a copy + return; + } + + foreach (int neighbor in graph[node]) + { // Recursive step + path.Add(neighbor); // Important + Dfs(neighbor); + path.RemoveAt(path.Count - 1); // Important - backtrack + } + } +} +``` + +## Go + +```go +func allPathsSourceTarget(graph [][]int) [][]int { + paths := [][]int{} + path := []int{0} // Important - start with node 0 + + var dfs func(int) + dfs = func(node int) { + if node == len(graph) - 1 { // Base case + // Important - make a deep copy of the path + paths = append(paths, append([]int(nil), path...)) + return + } + + for _, neighbor := range graph[node] { // Recursive step + path = append(path, neighbor) // Important + dfs(neighbor) + path = path[:len(path) - 1] // Important - backtrack + } + } + + dfs(0) + + return paths +} +``` + +## Ruby + +```ruby +# @param {Integer[][]} graph +# @return {Integer[][]} +def all_paths_source_target(graph) + @paths = [] + @graph = graph + @path = [0] # Important - start with node 0 + + dfs(0) + + @paths +end + +def dfs(node) + if node == @graph.length - 1 # Base case + @paths << @path.clone # Important - make a copy + return + end + + @graph[node].each do |neighbor| # Recursive step + @path << neighbor # Important + dfs(neighbor) + @path.pop # Important - backtrack + end +end +``` + +## Other languages + +```java +// Welcome to create a PR to complete the code of this language, thanks! +``` + 亲爱的力扣人,为了您更好的刷题体验,请访问 [LeetCodePython.com](https://leetcodepython.com/zh)。 本站敢称力扣题解最佳实践,终将省你大量刷题时间!