LeetCode 337. 打家劫舍 III

我的LeetCode:https://leetcode-cn.com/u/ituring/

我的LeetCode刷题源码[GitHub]:https://github.com/izhoujie/Algorithmcii

LeetCode 337. 打家劫舍 III

题目

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在__不触动警报的情况下__,小偷一晚能够盗取的最高金额。
示例 1:

输入: [3,2,3,null,3,null,1]

     3
    / \
   2   3
    \   \ 
     3   1

输出: 7 
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

示例 2:

输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路

一定要先读懂题目意思:不能抢任意两个连着的节点,或者说,对于一个节点,要么抢当前节点及其左右节点的子节点(孙子节点),要么不抢当前节点而抢其左右直接子节点;

思路1-基于上面的题意,用dp+递归的方式解决;

该思路是使用节点记录中间值:

  1. 空节点返回0值;
  2. 递归计算得到左右节点的各自最大值;
  3. 在往上一层节点时,计算取以下的最大值:
    • 抢当前节点极其左右节点的子节点的各自最大值之和(孙子节点);
    • 不抢当前节点只抢左右子节点的各自最大值之和(直接节点);

思路2-与1相同,但使用数组来记录中间数据;

数组索引0表示不抢当前节点时最大值,1表示抢当前节点时的最大值,其他流程思路与1无异;

算法源码示例

package leetcode;

/**
 * @author ZhouJie
 * @date 2020年3月24日 下午9:07:56 
 * @Description: 337. 打家劫舍 III
 * 
 *
 */
public class LeetCode_0337 {

}

// Definition for a binary tree node.
class TreeNode_0337 {
	int val;
	TreeNode_0337 left;
	TreeNode_0337 right;

	TreeNode_0337(int x) {
		val = x;
	}
}

class Solution_0337 {
	/**
	 * @author: ZhouJie
	 * @date: 2020年3月31日 下午1:48:54 
	 * @param: @param root
	 * @param: @return
	 * @return: int
	 * @Description: 1-对于一个节点来说,当前能偷到的最大值有两个可能:
	 * 				- 偷当前节点以及其左右节点的子节点中的各自最大值之和(4个孙子节点各自的最大值之和);
	 * 				- 不偷当前节点,偷左右子节点各自的最大值之和;
	 * 				- 子节点及孙子节点是一个可递归解决的问题;
	 *
	 */
	public int rob_1(TreeNode_0337 root) {
		TreeNode_0337 left = new TreeNode_0337(0);
		TreeNode_0337 right = new TreeNode_0337(0);
		return robMax_1(root, left, right);
	}

	/**
	 * @author: ZhouJie
	 * @date: 2020年3月31日 下午1:56:46 
	 * @param: @param root
	 * @param: @param left
	 * @param: @param right
	 * @param: @return
	 * @return: int
	 * @Description: 1-dp+递归(节点记录值);
	 *
	 */
	private int robMax_1(TreeNode_0337 root, TreeNode_0337 left, TreeNode_0337 right) {
		if (root == null) {
			return 0;
		} else {
			// 四个孙子节点
			TreeNode_0337 ll, lr, rl, rr;
			ll = new TreeNode_0337(0);
			lr = new TreeNode_0337(0);
			rl = new TreeNode_0337(0);
			rr = new TreeNode_0337(0);
			// 左右子节点的最大值
			left.val = robMax_1(root.left, ll, lr);
			right.val = robMax_1(root.right, rl, rr);
			return Math.max(root.val + ll.val + lr.val + rl.val + rr.val, left.val + right.val);
		}
	}

	/**
	 * @author: ZhouJie
	 * @date: 2020年3月31日 下午1:57:30 
	 * @param: @param root
	 * @param: @return
	 * @return: int
	 * @Description: 2-与1的区别仅在于使用数组保存中间值;
	 *
	 */
	public int rob_2(TreeNode_0337 root) {
		int[] rob = robMax_2(root);
		return Math.max(rob[0], rob[1]);
	}

	private int[] robMax_2(TreeNode_0337 root) {
		if (root == null) {
			// 空节点返回空数组给上层计算用
			return new int[2];
		}
		// 左右子节点最终rob数组
		int[] left = robMax_2(root.left);
		int[] right = robMax_2(root.right);
		int[] robbing = new int[2];
		// 最终做一次选择,不抢当前根节点(即左右子节点可抢可不抢),那么就从左右子节点中各选择抢与不抢的最大值即可;
		robbing[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
		// 若抢当前跟节点,则只能选择左右子节点不抢时的值并加上当前根节点的值
		robbing[1] = left[0] + right[0] + root.val;
		return robbing;
	}
}

你可能感兴趣的:(LeetCode 337. 打家劫舍 III)