LeetCode刷题 -- DFS

深度优先搜索算法,depth-first-search DFS,是一种用于遍历或者搜索树、图的算法,这个算法会尽可能深的去搜索树的分支。

深度优先搜索是图论中的经典算法,DFS基于递归思想,实质是一种借助递归实现的枚举。

基本代码结构

void dfs(int step) {
    // 判断边界;
    // 尝试每一种可能;
    int len = getXxxLen();//define your own length
    for (int i = 1; i <= len; ++i) {
        //继续下一步
        dfs(step + 1);
    }
    // 返回;
}

例题

113. 路径总和 II

题目

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

分析:

这题的思路就是深度遍历,利用递归的方式,从根节点一直遍历到叶子节点,然后记录所有满足要求的路径。

问题点:

1、那种路径是满足要求的?

        ---> 只要从根节点一直加到叶子节点,和刚好等于目标值就是满足要求

2、满足要求的路径,怎么存储?

        ---> 定义一个LinkedList,用于存储遍历路径上的所有节点的值

                遍历到某个节点时,就把这个节点的值添加进去

                如果某个节点不符合要求,要跳出这个节点,遍历其他节点,就把这个不符合要求的节点的值,从当前LinkedList中删除,也就是删除最后一次插入的那个值。

        

代码

    // 定义全局变量,用于方法返回值
    List> result = new ArrayList<>();
    // 定义一个LinkedList,用于存储单个遍历路径上的数值
    LinkedList list = new LinkedList<>();
    public List> pathSum(TreeNode root, int targetSum) {
        // 定义路径上所有的和
        int sum = 0;
        dfs(root, targetSum, sum);
        return result;
    }
    // 定义dfs方法
    void dfs(TreeNode root, int targetSum, int sum) {
        if (root == null) {
            return;
        }
        // 将当前节点的value值,存到链表中
        list.add(root.val);
        // 路径和加上当前节点的值
        sum += root.val;
        
        // 如果当前节点为叶子节点,并且此时路径和刚好等于目标值,那么就将数据list添加到result中
        if (root.left == null && root.right == null && sum == targetSum) {
            // 注意这个地方,添加的时候,要new一个list,不能把当前list传进去,
            // 否则dfs跳出的时候,list里面的值也没了
            result.add(new ArrayList<>(list));
        }
        // 递归调用dfs方法
        dfs(root.left, targetSum, sum);
        dfs(root.right, targetSum, sum);
        
        // 关键这一步,如果跳出了当前的dfs方法,那么要把当前节点的值,从list中移除。
        list.removeLast();
    }
class Solution:
    def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
        res = []
        cur_list = []

        def dfs(node, sum_value, target):
            if not node:
                return
            cur_list.append(node.val)
            sum_value += node.val

            if not node.left and not node.right and sum_value == target:
                res.append(list(cur_list))

            dfs(node.left, sum_value, target)
            dfs(node.right, sum_value, target)

            cur_list.pop()

        dfs(root, 0, targetSum)
        return res

 124. 二叉树中的最大路径和

题目

路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点

分析:

LeetCode刷题 -- DFS_第1张图片

 一个二叉树,a为当前递归调用中的传入的根节点(左右节点分别是已经计算好的最大子路径),注意题目,同一个节点在一条路径中最多出现一次,因此,最大路径可能情况是

  1. a节点不连接父节点
    1. a
    2. a+left
    3. a+right
    4. a + left + right 
  2.   a节点连接父节点,为了保证路径上节点最多出现一次,那么左右节点就只能选择一个,
    1. parent + a + right  -- right大则选择right
    2. parent + a + left   -- left大则选择left

此外,因为节点有负值的情况,因此,如果节点为负的情况下,要想办法舍弃负值

代码

public class Test124 {
    // 定义一个最大值,因为节点的值可能为负
    // 因此初始化值,设置为Integer.MIN_VALUE
    int maxValue = Integer.MIN_VALUE;

    public int maxPathSum(TreeNode root) {
        getMaxPath(root);
        return maxValue;
    }

    // dfs方法,寻找最大的子节点路径path
    private int getMaxPath(TreeNode root) {
        // 当前节点为null,对路径和没有贡献,因此返回值0;
        if (null == root) {
            return 0;
        }
        // 递归调用左右子节点
        // 如果子节点求出来的路径和最大值为负数,对路径和没有贡献
        // 因此,取0 和 返回值 较大的一个值
        int leftMaxPath = Math.max(0, getMaxPath(root.left));
        int rightMaxPath = Math.max(0, getMaxPath(root.right));
        
        // 更新最大路径和,当前要比较的对象是本节点值,加上左右节点最大贡献值
        maxValue = Math.max(maxValue, root.val + leftMaxPath + rightMaxPath);
        
        // 返回值,注意此处,因为同一个节点在一条路径只能出现一次
        // 因此,返回值要设置为,当前节点的值 加上 左右 节点 比较大的贡献哪个路径。
        return root.val + Math.max(leftMaxPath, rightMaxPath);
    }
}
class Solution:
    def __init__(self):
        self.max_value = None

    def maxPathSum(self, root: Optional[TreeNode]) -> int:
        self.max_value = -(sys.maxsize - 1)

        def dfs(node):
            if not node:
                return 0

            left_max = max(0, dfs(node.left))
            right_max = max(0, dfs(node.right))

            self.max_value = max(self.max_value, node.val + left_max + right_max)

            return node.val + max(left_max, right_max)

        dfs(root)
        return self.max_value

130. 被围绕的区域

题目:

给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

分析:

这到提要理解清楚,什么是被X围绕的O,弄清题意之后可知:对于所有边界上的O以及和边界上的O直接相连的O,都不属于被X围绕,而剩下的O,就是被X围绕了。

根据这个思路,可以进行如下解:

首先遍历边界上的值,将所有的O,并且与这个O直接相连的O,全部找出来(可以用到深DFS方式)并标记为A

此时,当前数组上所有的O就是被X包围的,可以直接改成X,然后在吧标记为A的,在改回O

注意:O和0很像,键盘也离得近,注意注意在注意o_O;

代码:

public class Test130 {
    public void solve(char[][] board) {
        if (board == null) {
            return;
        }
        int m = board.length;
        int n = board[0].length;

        // 上下两行进行遍历
        for (int i = 0; i < board[0].length; i++) {
            edgeO(board, 0, i);
            edgeO(board, m - 1, i);
        }
        // 左右两列进行遍历
        for (int i = 1; i < m - 1; i++) {
            edgeO(board, i, 0);
            edgeO(board, i, n - 1);
        }

        // 最后遍历整个数组,将之前没有标记为A的,改成X
        // 将之前标记为A的,改回为O
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
                if (board[i][j] == 'A') {
                    board[i][j] = 'O';
                }
            }
        }

    }

    public void edgeO(char[][] board, int x, int y) {
        int m = board.length;
        int n = board[0].length;
        // 数组不越界,并且值为O的时候,将值先标记为A
        if (x < m && x >= 0 && y < n && y >= 0 && board[x][y] == 'O') {
            board[x][y] = 'A';
            edgeO(board, x - 1, y);
            edgeO(board, x + 1, y);
            edgeO(board, x, y - 1);
            edgeO(board, x, y + 1);
        }
    }
}
class Solution:
    def solve(self, board: List[List[str]]) -> None:
        m, n = len(board), len(board[0])

        def dfs(i, j):
            if i < 0 or j < 0 or i >= m or j >= n or board[i][j] != "O":
                return
            board[i][j] = "A"
            dfs(i - 1, j)
            dfs(i + 1, j)
            dfs(i, j - 1)
            dfs(i, j + 1)

        for i in range(n):
            if board[0][i] == "O":
                dfs(0, i)
            if board[m - 1][i] == "O":
                dfs(m - 1, i)

        for i in range(m):
            if board[i][0] == "O":
                dfs(i, 0)
            if board[i][n - 1] == "O":
                dfs(i, n - 1)

        for i in range(m):
            for j in range(n):
                # 注意这个改变的顺序,如果先从A改成O,那么下面又会从O改成X,就出问题了
                if board[i][j] == "O":
                    board[i][j] = "X"
                if board[i][j] == "A":
                    board[i][j] = "O"

你可能感兴趣的:(深度优先,leetcode,算法)