leetcode:337. 打家劫舍 III
题目
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
示例 1:![[中等] 337. 打家劫舍 III - 图1](/uploads/projects/liangduo-rjrcs@ggu4wq/6dde179ec5f416146954f0d61a983b80.jpeg)
输入: root = [3,2,3,null,3,null,1]输出: 7解释: 小偷一晚能够盗取的最高金额 3 + 3 + 1 = 7
示例 2:![[中等] 337. 打家劫舍 III - 图2](/uploads/projects/liangduo-rjrcs@ggu4wq/9e42b705917469f9b9fba7daa81709b2.jpeg)
输入: root = [3,4,5,1,3,null,1]输出: 9解释: 小偷一晚能够盗取的最高金额 4 + 5 = 9
示例 3:
2/ \1 3\4输入: root = [2,1,3,null,4]输出: 7解释: 小偷一晚能够盗取的最高金额 3 + 4 = 7
解答 & 代码
二叉树后序遍历 + 动态规划(树形 dp)
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/class Solution {private:// 递归后序遍历二叉树// 返回值:长为 2 的数组 {not_rob_result, rob_result}// - not_rob_result:不偷 root 节点的最大金钱// - rob_result:偷 root 节点的最大金钱// 以上指的都是偷以 root 为根节点的二叉树的最大金钱,区别再与偷不偷 root 这个节点vector<int> robTree(TreeNode* root){// 递归结束条件 / dp 初始化:若当前节点为 NULL,那么偷不偷都是 0(没有可偷的)if(root == NULL)return vector<int>{0, 0};// 递归遍历左、右子树// 递归得到左子树这棵树,偷左节点和不偷左节点的最大金钱vector<int> left_result = robTree(root->left);// 递归得到右子树这棵树,偷右节点和不偷右节点的最大金钱vector<int> right_result = robTree(root->right);// 后序处理得到当前节点的结果(当前这棵树可偷的最大金钱)// 偷 root 节点的最大金钱:偷当前节点,左右子节点就不能偷int rob_result = root->val + left_result[0] + right_result[0];// 不偷 root 节点的最大金钱:不偷当前节点,左右子节点就可以偷,// 但是注意:左、右子节点偷不偷还要看偷、不偷哪个收益大,取最大值int not_rob_result = max(left_result[0], left_result[1]) + max(right_result[0], right_result[1]);return vector<int>{not_rob_result, rob_result};}public:int rob(TreeNode* root) {vector<int> result = robTree(root);return max(result[0], result[1]);}};
复杂度分析:设二叉树节点总数 N
- 时间复杂度 O(N):后序遍历,每个节点只遍历了一次
- 空间复杂度 O(logN):递归栈空间复杂度
执行结果:
执行结果:通过执行用时:24 ms, 在所有 C++ 提交中击败了 40.33% 的用户内存消耗:31.1 MB, 在所有 C++ 提交中击败了 15.07% 的用户
换种写法:返回值改为 pair 的形式:
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/class Solution {private:// 后序遍历 + 动态规划(树形 dp)pair<int, int> postOrder(TreeNode* root){if(root == nullptr)return make_pair(0, 0);pair<int, int> dpLeft = postOrder(root->left);pair<int, int> dpRight = postOrder(root->right);int notRobRoot = max(dpLeft.first, dpLeft.second) + max(dpRight.first, dpRight.second);int robRoot = root->val + dpLeft.first + dpRight.first;// cout << "root = " << root->val << ", notRobRoot = " << notRobRoot << ", robRoot = " << robRoot << endl;return make_pair(notRobRoot, robRoot);}public:int rob(TreeNode* root) {pair<int, int> dpRoot = postOrder(root);return max(dpRoot.first, dpRoot.second);}};
复杂度分析:设二叉树节点总数 N
- 时间复杂度 O(N):后序遍历,每个节点只遍历了一次
- 空间复杂度 O(logN):递归栈空间复杂度
执行结果:
执行结果:通过执行用时:16 ms, 在所有 C++ 提交中击败了 75.94% 的用户内存消耗:21.5 MB, 在所有 C++ 提交中击败了 99.81% 的用户
