leetcode dfs_深度优先搜索:具有6个Leetcode示例的DFS图遍历指南

本文是一篇关于深度优先搜索(DFS)的教程,讲解如何使用DFS解决LeetCode上的中等和困难级别的树和图问题。通过DFS的递归特性,介绍如何遍历二叉树(前序、中序、后序)和解决无向图的连接组件问题,例如144.二叉树前序遍历、145.二叉树后序遍历、94.二叉树有序遍历等。文章提供了DFS的实现策略和具体代码示例。
摘要由CSDN通过智能技术生成

leetcode dfs

Have you ever solved a real-life maze? The approach that most of us take while solving a maze is that we follow a path until we reach a dead end, and then backtrack and retrace our steps to find another possible path.

您是否解决了现实生活中的迷宫? 我们大多数人在解决迷宫时采取的方法是:沿着一条路直到到达死胡同,然后回溯并追溯我们的步骤以找到另一条可能的路。

This is exactly the analogy of Depth First Search (DFS). It's a popular graph traversal algorithm that starts at the root node, and travels as far as it can down a given branch, then backtracks until it finds another unexplored path to explore. This approach is continued until all the nodes of the graph have been visited.

这正是深度优先搜索(DFS)的类比。 这是一种流行的图形遍历算法,从根节点开始,一直沿给定分支向下传播,然后回溯直到找到另一条未探索的路径。 继续进行此方法,直到访问了图的所有节点为止。

In today’s tutorial, we are going to discover a DFS pattern that will be used to solve some of the important tree and graph questions for your next Tech Giant Interview! We will solve some Medium and Hard Leetcode problems using the same common technique.

在今天的教程中,我们将发现一种DFS模式,该模式将用于解决您下一次Tech Giant采访中的一些重要的树形图问题! 我们将使用相同的通用技术解决一些中度和硬性Leetcode问题。

So, let’s get started, shall we?

所以,让我们开始吧?

实作 (Implementation)

Since DFS has a recursive nature, it can be implemented using a stack.

由于DFS具有递归性质,因此可以使用堆栈来实现。

DFS Magic Spell:

DFS魔法:

  1. Push a node to the stack

    将节点推送到堆栈
  2. Pop the node

    弹出节点
  3. Retrieve unvisited neighbors of the removed node, push them to stack

    检索已删除节点的未访问邻居,将其压入堆栈
  4. Repeat steps 1, 2, and 3 as long as the stack is not empty

    只要堆栈不为空,重复步骤1、2和3

图遍历 (Graph Traversals)

In general, there are 3 basic DFS traversals for binary trees:

通常,对二进制树有3种基本的DFS遍历:

  1. Pre Order: Root, Left, Right OR Root, Right, Left

    预购:根,左,右根,右,左

  2. Post Order: Left, Right, Root OR Right, Left, Root

    发布顺序:左,右,根右,左,根

  3. In order: Left, Root, Right OR Right, Root, Left

    顺序:左,根,右右,根,左

144.遍历二叉树(难度:中) (144.  Binary Tree Preorder Traversal (Difficulty: Medium))

To solve this question all we need to do is simply recall our magic spell. Let's understand the simulation really well since this is the basic template we will be using to solve the rest of the problems.

要解决这个问题,我们要做的就是简单地回忆起我们的魔咒。 让我们很好地理解模拟,因为这是我们将用来解决其余问题的基本模板

At first, we push the root node into the stack. While the stack is not empty, we pop it, and push its right and left child into the stack.

首先,我们将根节点推入堆栈。 当堆栈不为空时,我们将其弹出,然后将其左右子项推入堆栈。

As we pop the root node, we immediately put it into our result list. Thus, the first element in the result list is the root (hence the name, Pre-order). The next element to be popped from the stack will be the top element of the stack right now: the left child of root node. The process is continued in a similar manner until the whole graph has been traversed and all the node values of the binary tree enter into the resulting list.

当我们弹出根节点时,我们立即将其放入结果列表。 因此,结果列表中的第一个元素是根(因此,其名称为Pre-order)。 从堆栈中弹出的下一个元素将是当前堆栈的顶部元素:根节点的左子节点。 以相似的方式继续该过程,直到遍历了整个图并且二叉树的所有节点值都进入结果列表为止。

145.二叉树后序遍历(难度:困难) (145. Binary Tree Postorder Traversal (Difficulty: Hard))

Pre-order traversal is root-left-right, and post-order is right-left-root. This means post order traversal is exactly the reverse of pre-order traversal.

顺序遍历是root-left-right ,而顺序遍历是right-left-root 。 这意味着后订单遍历与前订单遍历完全相反。

So one solution that might come to mind right now is simply reversing the resulting array of pre-order traversal. But think about it – that would cost O(n) time complexity to reverse it.

因此,现在可能想到的一种解决方案就是简单地逆转最终的遍历结果数组。 但是考虑一下–反转它会花费O(n)时间复杂度。

A smarter solution is to copy and paste the exact code of the pre-order traversal, but put the result at the top of the linked list (index 0) at each iteration. It takes constant time to add an element to the head of a linked list. Cool, right?

一个更聪明的解决方案是复制并粘贴预遍历的确切代码,但是在每次迭代时将结果放在链接列表的顶部(索引0)。 向链接列表的开头添加元素需要花费固定的时间。 酷吧?

94.二叉树有序遍历(难度:中) (94. Binary Tree Inorder Traversal (Difficulty: Medium))

Our approach to solve this problem is similar to the previous problems. But here, we will visit everything on the left side of a node, print the node, and then visit everything on the right side of the node.

我们解决此问题的方法与先前的问题类似。 但是在这里,我们将访问节点左侧的所有内容,打印该节点,然后访问该节点右侧的所有内容。

323.无向图中的连接组件数 (难度:中) (323. Number of Connected Components in an Undirected Graph
(Difficulty: Medium)
)

Our approach here is to create a variable called ans that stores the number of connected components.

我们这里的方法是创建一个名为ans的变量,该变量存储已连接组件的数量。

First, we will initialize all vertices as unvisited. We will start from a node, and while carrying out DFS on that node (of course, using our magic spell), it will mark all the nodes connected to it as visited. The value of ans will be incremented by 1.

首先,我们将所有顶点初始化为未访问。 我们将从一个节点开始,并且在该节点上执行DFS时(当然,使用我们的魔术咒语),它将把与其连接的所有节点标记为已访问。 ans的值将增加1。

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class NumberOfConnectedComponents {
    public static void main(String[] args){
        int[][] edge = {{0,1}, {1,2},{3,4}};
        int n = 5;
        System.out.println(connectedcount(n, edge));

    }

    public static int connectedcount(int n, int[][] edges) {

        boolean[] visited = new boolean[n];
        List[] adj = new List[n];
        for(int i=0; i<adj.length; i++){
            adj[i] = new ArrayList<Integer>();
        }

        // create the adjacency list
        for(int[] e: edges){
            int from = e[0];
            int to = e[1];
            adj[from].add(to);
            adj[to].add(from);

        }
        Stack<Integer> stack = new Stack<>();
        int ans = 0; // ans = count of how many times DFS is carried out

        // this for loop through the entire graph
        for(int i = 0; i < n; i++){
            // if a node is not visited
            if(!visited[i]){
                ans++;
                //push it in the stack
                stack.push(i);

             
                while(!stack.empty()) {

                    int current = stack.peek();
                    stack.pop(); //pop the node
                    visited[current] = true; // mark the node as visited

                    List<Integer> list1 = adj[current];

        // push the connected components of the current node into stack
                    for (int neighbours:list1) {
                        if (!visited[neighbours]) {
                            stack.push(neighbours);
                        }
                    }
                }

        }
    }
        return ans;
    }
}

200.岛屿数量(难度:中) (200. Number of Islands (Difficulty: Medium))

This falls under a general category of problems where we have to find the number of connected components, but the details are a bit tweaked.

这属于一般问题类别,我们必须找到已连接组件的数量,但细节有所调整。

Instinctually, you might think that once we find a “1” we initiate a new component. We do a DFS from that cell in all 4 directions (up, down, right, left) and reach all 1’s connected to that cell. All these 1's connected to each other belong to the same group, and thus, our value of count is incremented by 1. We mark these cells of 1's as visited and move on to count other connected components.

本能地,您可能会认为,一旦找到“ 1”,便会启动一个新组件。 我们在该单元的所有4个方向(上,下,右,左)上执行DFS,并达到与该单元相连的所有1。 所有这些相互连接的1属于同一组,因此,我们的count值加1。我们将1的这些单元格标记为已访问,然后继续对其他连接的组件进行计数。

547.朋友圈(难度:中) (547. Friend Circles (Difficulty: Medium))

This also follows the same concept as finding the number of connected components. In this question, we have an NxN matrix but only N friends in total. Edges are directly given via the cells so we have to traverse a row to get the neighbors for a specific "friend".

这也遵循与查找已连接组件数相同的概念。 在这个问题中,我们有一个NxN矩阵,但总共只有N个朋友。 边缘是直接通过单元格给出的,因此我们必须遍历一行以获取特定“朋友”的邻居。

Notice that here, we use the same stack pattern as our previous problems.

请注意,在这里,我们使用与先前问题相同的堆栈模式。

That's all for today! I hope this has helped you understand DFS better and that you have enjoyed the tutorial. Please recommend this post if you think it may be useful for someone else!

今天就这些! 我希望这可以帮助您更好地了解DFS,并希望您喜欢本教程。 如果您认为此帖子可能对其他人有用,请推荐此帖子!

翻译自: https://www.freecodecamp.org/news/dfs-for-your-next-tech-giant-interview/

leetcode dfs

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值