LeetCode162--打家劫舍III(L337)、比特位计数(L338)

1、打家劫舍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.
//
// Related Topics 树 深度优先搜索 动态规划 二叉树

如此聪明的小偷!!!
这个时候我们就需要维护两个数组,一个是w[o]另一个是n[o],前者代表在选择o节点的情况下能够达到的钱数的最大值,后者代表在不选o节点的情况下钱数能够到达的最大值。
然后我们用r和l来分别表示o节点的左节点和右节点
所以有w[o]=n[l]+n[r],还有n[o]=max(w[l],n[l])+max(w[r],n[r])
所以最后的程序为:

class Solution {
	Map<TreeNode, Integer> w = new HashMap<>();
	Map<TreeNode, Integer> n = new HashMap<>();
    public int rob(TreeNode root) {
    	//想想我们维护两个数组一个是w[]另一个是n[]
		//w[o]代表选中节点o的情况下,以o为根节点的支路能窃取的最大值
		//n[o]代表不选中节点o的情况下,以o为根节点的支路能窃取的最大值
		//o的左右节点分别为l和r,所以就有
		//w[o]=n[l]+n[r];
		//n[o]=max(w[l],n[l])+max(w[r],n[r])
		dfs(root);
		return Math.max(w.getOrDefault(root, 0), n.getOrDefault(root, 0));
	}
	private void dfs(TreeNode node){
    	if(node == null){
    		return;
		}
    	dfs(node.left);
    	dfs(node.right);
    	w.put(node, node.val+n.getOrDefault(node.left, 0)+n.getOrDefault(node.right,0));
    	n.put(node, Math.max(w.getOrDefault(node.left,0),n.getOrDefault(node.left,0))+
				Math.max(w.getOrDefault(node.right,0),n.getOrDefault(node.right, 0)));
	}
}

2、比特位计数

//给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
//
// 示例 1:
//
// 输入: 2
//输出: [0,1,1]
//
// 示例 2:
//
// 输入: 5
//输出: [0,1,1,2,1,2]
//
// 进阶:
//
//
// 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
// 要求算法的空间复杂度为O(n)。
// 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。
//
// Related Topics 位运算 动态规划

方法一:用自带的函数

public int[] countBits(int n) {
        //方法一:Java的内置函数
        int[] ints = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            ints[i] = Integer.bitCount(i);
        }
        return ints;
    }

方法二:使用Brian Keinighan算法

public int[] countBits(int n) {
        //方法二:Brian Kernighan算法通过x=x&(x-1)将二进制的最后一位变成0
        int[] ints = new int[n + 1];
        for (int i = 0; i <= n; i++) {
            ints[i] = countOne(i);
        }
        return ints;
    }
    private int countOne(int x){
        int count = 0;
        while(x != 0){
            //将二进制的最后一位1变成0
            x = x&(x-1);
            count++;
        }
        return count;
    }

方法三:动态规划
找到2的n次幂这样的数,比如10,100,1000,这样我们得到了我们每个阶段数的最高有效位,bits[i]=bits[i-highBit]+1

public int[] countBits(int n) {
       //方法三:动态规划
        int[] bits = new int[n + 1];
        int highBit = 0;
        for (int i = 0; i <= n; i++) {
            //当且仅当这个数为2的n次幂时才满足这个条件
            if((i&(i-1)) == 0){
                highBit = i;
            }
            bits[i] = bits[i-highBit]+1;
        }
        return bits;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值