社区内互不相邻的房间内财物之和的最大值

一个很有意思的问题,一个社区,所有的房子构成一棵二叉树,每个房子里有一定价值的财物,这棵二叉树有一个根节点root。如果相邻的两座房子同时被进入,就会触发警报。一个小偷,最初只能访问root节点,并可以通过二叉树的边访问房子(注:访问不意味着进入),请问不触发警报的前提下他能偷到的财物的最大价值是多少?

以下面这棵二叉树为例,最多能偷走3+3+1=7的财物
[color=red]3[/color]
/ \
2 3
\ \
[color=red]3 1[/color]

分析:
这个问题乍一看上去可能没什么思路,但是如果是用递归,可以很优雅的解决这个问题,这需要读者对递归有比较深刻的理解。下面给出解决这个问题的java代码:
package houseRobberIII;

import java.util.HashMap;
import java.util.Map;

import common.TreeNode;

public class Solution {
//使用一个cache 缓存以每个节点为根节点的rob方法返回值,减少计算量
Map<TreeNode, Integer> cache = new HashMap<TreeNode, Integer>();
public int rob(TreeNode root) {
//如果当前节点为空 直接返回0
if(null == root){
return 0;
}
//首先查看缓存中有没有这个节点的rob方法返回值
if(null != cache.get(root)){
return cache.get(root);
}
//计算当前节点左孩子的rob方法返回值
int maxLeft = rob(root.left);
//计算当前节点右孩子的rob方法返回值
int maxRight = rob(root.right);
int maxLeftLeft = 0;
int maxLeftRight = 0;
//如果当前节点有左孩子
if(null != root.left){
//计算其左孩子的左孩子的rob值
maxLeftLeft = rob(root.left.left);
//计算其左孩子的右孩子的rob值
maxLeftRight = rob(root.left.right);
}
int maxRightLeft = 0;
int maxRightRight = 0;
//如果当前节点有右孩子
if(null != root.right){
//计算其右孩子的左孩子的rob值
maxRightLeft = rob(root.right.left);
//计算其右孩子的右孩子的rob值
maxRightRight = rob(root.right.right);
}
//不偷当前节点能偷到的财物的最大值
int notIncludeCurrentNodeMax = maxLeft + maxRight;
//偷当前节点能偷到的财物的最大值
int includeCurrentNodeMax = maxLeftLeft + maxLeftRight + maxRightLeft + maxRightRight + root.val;
//以其中的较大值作为当前节点的rob方法返回值
int res = notIncludeCurrentNodeMax > includeCurrentNodeMax ? notIncludeCurrentNodeMax : includeCurrentNodeMax;
//缓存当前节点的rob方法返回值
cache.put(root, res);
return res;
}
}

package common;

/**
* 二叉树节点类
* @author xuguanglv
*
*/
public class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int x) { val = x; }
}

这个解法中比较重要的一点是使用了cache进行动态规划,使整个算法的时间复杂度为O(V+E),其中V为二叉树的节点的数量,E为二叉树的边的数量;如果不使用cache来缓存每一个节点的rob方法返回值,每次访问某个节点,都要重复计算一次rob值,最后的时间复杂度将会是指数级的,显然不可接受的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值