刷题计划——深度优先搜索DFS(一)

10 篇文章 0 订阅

面试题13. 机器人的运动范围(中等)

题目:

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:

输入:m = 2, n = 3, k = 1
输出:3
示例 2:

输入:m = 3, n = 1, k = 0
输出:1
提示:

1 <= n,m <= 100
0 <= k <= 20

首先分析题意,机器人从坐标为**[0][0]** 的位置开始,可以向上下左右四个方向前进,当所在位置行坐标和列坐标的数位之和大于数字K, 即表示该位置机器人可达。

通俗来说,可以将机器人所在的“地图”用一个二维数组表示。很明显,对于二维数组中的数位而言,其大致呈现对角线分布的趋势。
在这里插入图片描述
因此可以推出机器人通过向右和向左的查询即可访问所有的可达解。

此题有BFS, DFS, 动态规划等多种求解办法。

对于DFS而言,其本质可以通过递归的方式来求得最终解。

class Solution {
private:
	// 使用一个100*100的 “地图”
    int _map[100][100];
public:
    int movingCount(int m, int n, int k) {
        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                _map[i][j] = 0;
        return DFS(m, n, 0, 0, k);
    }

    int DFS(int m, int n, int x, int y, int &k){
    	// _map[x][y]为1说明这个节点已经被遍历过,无需重复计算
    	// x < 0 y > 0 都是边界问题,一旦超过地图边界则机器人不可达
    	// 因为题目给出的地图是 m*n, 因此m与n同样属于地图边界
    	// (x / 10 + x % 10 + y / 10 + y % 10) > k 判断该位置的数位之和是否大于k
        if(_map[x][y] == 1 || x < 0 || y < 0 || x >= m || y >= n ||  (x / 10 + x % 10 + y / 10 + y % 10) > k){
            return 0;
        }

		// 若正常则说明该位置可达,将该位置标记为1,说明可达并且已经到达过
        _map[x][y] = 1;

        return DFS(m, n, x+1, y, k) + DFS(m, n, x, y+1, k) + 1;
    }
};

113. 路径总和 II (中等)

题目:

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

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 225
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

和二叉树的遍历一样,针对二叉树的路径问题同样要遍历整个二叉树。

在此题中,若要求得路径和为sum的路径,必须满足几个条件:

  • 路径的终点必须为叶子节点,也就是 root->left == nullptr && root->right == nullptr;
  • 路径的和必须等于sum, 也就是 pathSum == sum;

只有满足这两个条件才是一个有效的路径。

需要注意的是,在使用完一个节点后,将该节点从path 中弹出,即表示通过该节点的所有可能路径已经遍历结束

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
	//	res 存储有效的路径
    vector<vector<int>> res;
    //	path 存储路径的节点
    vector<int> path;

public:
    void DFS(TreeNode *root, int sum,int &target){
        if(!root){
            return;
        }
        path.push_back(root->val);
        sum += root->val;

		//	满足有效路径的定义,将该路径添加到res中
        if(!root->left && !root->right && target == sum){
            res.push_back(path);
            path.pop_back();
            return;
        }

		//	遍历左右子树
        DFS(root->left, sum, target);
        DFS(root->right, sum, target);

		//	若没有有效路径,则将该节点在路径中弹出
        path.pop_back();
    }

    vector<vector<int>> pathSum(TreeNode* root, int sum) {
        DFS(root, 0, sum);
        return res;
    }
};

面试题54. 二叉搜索树的第k大节点(简单)

题目:

给定一棵二叉搜索树,请找出其中第k大的节点。

示例 1:

输入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
输出: 4
示例 2:

输入: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
输出: 4
 

限制:

1 ≤ k ≤ 二叉搜索树元素个数

搜索二叉树的一个重要性质:左子树的所有节点数值小于根节点数值右子树的所有节点数值大于根节点数值

这样就可以通过变相的先序遍历来解决该问题。

先序遍历的遍历顺序为左子树 -> 根节点 -> 右子树,若使用先序遍历遍历二叉搜索树,毫无疑问第一个节点为树中最小的元素,依次增加。

利用这个性质,将先序遍历的遍历顺序改为右子树 -> 根节点 -> 左子树,这样就解决了该问题。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<int> res;
public:
    void DFS(TreeNode *root, int &k){
        if(!root){
            return;
        }

        DFS(root->right, k);
        //	res[0] 一定是最大的元素,依次递减
        res.push_back(root->val);
        DFS(root->left, k);
    }

    int kthLargest(TreeNode* root, int k) {
        DFS(root, k);

        return res[k-1];
    }
};

其实还可以进一步优化,无需遍历树中的所有节点,我们需要的第k个最大元素,那么res.size == k 就可以保证需要的结果。

优化后的代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    vector<int> res;
public:
    void DFS(TreeNode *root, int &k){
        if(!root){
            return;
        }

		// 无需遍历所有节点
        if(res.size() == k){
            return;
        }

        DFS(root->right, k);
        res.push_back(root->val);
        DFS(root->left, k);
    }

    int kthLargest(TreeNode* root, int k) {
        DFS(root, k);

        return res[k-1];
    }
};

1302. 层数最深叶子节点的和(中等)

题目:

给你一棵二叉树,请你返回层数最深的叶子节点的和。

示例:
在这里插入图片描述

输入:root = [1,2,3,4,5,null,6,7,null,null,null,null,8]
输出:15
 

提示:

树中节点数目在 110^4 之间。
每个节点的值在 1100 之间。

解决此问题关键是如何确定所在节点的深度如何确定二叉树的最大深度

回顾二叉树的最大深度求解过程,使用递归方法最重要的一步depth = max(leftDepth, rightDepth) + 1; 使用类似的办法:

  • 每个节点有一个dep变量来标记该节点所处的深度,使用sum来记录层数最深叶子节点的和;
  • 对比目前节点深度与最大深度maxDepth
    – 若dep > maxDepth ,说明最大深度需要改变,将maxDepth改变为dep, 同时将sum重新计算;
    – 若dep == maxDepth, 说明当前节点处于最大深度,将当前节点值添加至sum中;
    – 若dep < maxDepth, 说明当前节点深度小于最大深度,直接跳过该节点;
  • 最终返回的sum一定是处于最大深度的节点数之和。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
private:
    int maxDepth;
    int sum;
public:
    void DFS(TreeNode *root, int dep){
        if(!root){
            return;
        }
        dep++;

        if(dep == maxDepth){
            sum += root->val;
        }
        if(dep > maxDepth){
            sum = root->val;
            maxDepth = dep;
        }

        DFS(root->left, dep);
        DFS(root->right, dep);
    }

    int deepestLeavesSum(TreeNode* root) {
        sum = 0;
        maxDepth = 0;
        DFS(root, 0);
        
        return sum;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值