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;
}