337-打家劫舍||
在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。
示例 1: 输入: [3,2,3,null,3,null,1]
3
/ \
2 3
\ \
3 1
输出: 7 解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
/**
用 f(o)表示选择 o 节点的情况下,o 节点的子树上被选择的节点的最大权值和;
g(o)表示不选择 o 节点的情况下,o 节点的子树上被选择的节点的最大权值和;
l和 r 代表 o 的左右孩子。
1.当 o 被选中时,o 的左右孩子都不能被选中,故 o 被选中情况下子树上被选中点的最大权值和:
为 l 和 r 不被选中的最大权值和相加,即 f(o)=g(l)+g(r)(即左孩子不被选中的最大权值和+右孩子被选中的最大权值和)
2.当 o 不被选中时,o 的左右孩子可以被选中,也可以不被选中。对于 o 的某个具体的孩子 x,
它对 o 的贡献是 x 被选中和不被选中情况下权值和的较大值。(o不被选中则左右孩子都可以被选中或者不被选中)
故 g(o)=max{f(l),g(l)}+max{f(r),g(r)}
我们发现无论是 f(o) 还是g(o),他们最终的值只和 f(l)、g(l)、f(r)、g(r)有关,所以对于每个节点,
我们只关心它的孩子节点们的 f 和 g 是多少。我们可以设计一个结构,表示某个节点的 f 和 g 值,
在每次递归返回的时候,都把这个点对应的 f 和 g 返回给上一级调用,这样可以省去哈希表的空间。
*/
public int rob(TreeNode root) {
int[] rootStatus = dfs(root);
return Math.max(rootStatus[0],rootStatus[1]);
}
private int[] dfs(TreeNode node){
//节点为空时,左右孩子权值始终是0
if(node==null){
return new int[]{0,0};
}
//后序遍历
//记录左、右孩子被选中和不被选中时的权值
int[] left = dfs(node.left);
int[] right = dfs(node.right);
//被选中时,左右孩子都不被选中的最大权值和
int select = node.val + left[1] + right[1];
//不被选中时,左右节点被选中或者不被选中的权值最大值之和
int noSelect = Math.max(left[0],left[1])+Math.max(right[0],right[1]);
return new int[]{select,noSelect};
}
}
338-比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2 输出: [0,1,1]
示例 2:
输入: 5 输出: [0,1,1,2,1,2]
来源:力扣(LeetCode)
class Solution {
/**
动态规划——最低有效位
用「一比特数」表示二进制表示中的 1 的数目。
令 bits[i] 表示 i 的「一比特数」
对于正整数 x,将其二进制表示右移一位,等价于将其二进制表示的最低位去掉,得到的数是 ⌊x/2⌋。
如果 bits[⌊x/2⌋]的值已知,则可以得到 bits[x]的值(即根据奇数和偶数二进制末尾分别是是0和1来计算):
1.如果 x 是偶数,则 bits[x]=bits[⌊x/2⌋]
2.如果 x 是奇数,则 bits[x]=bits[⌊x/2⌋]+1
上述两种情况可以合并成:bits[x]的值等于 bits[⌊x2⌋]的值加上 x 除以 2 的余数。
x 除以 2 的余数可以通过 x & 1得到,
因此有:bits[x]=bits[x>>1]+(x & 1)
*/
public int[] countBits(int n) {
int[] bits = new int[n+1];
for(int i=1;i<=n;i++){
//计算顺序是 0 1 2 3 4 5 因此计算后面x时,其x/2的状态已经被计算出来了
bits[i] = bits[i>>1] + (i&1);
}
return bits;
}
}
347-前k个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2 输出: [1,2]
示例 2:
输入: nums = [1], k = 1 输出: [1]
//使用java自带的堆PriorityQueue
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
//统计数字出现的个数
for(int num:nums){
map.put(num,map.getOrDefault(num,0)+1);
}
//构建小根堆,用频率当前排序的标准
PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer m, Integer n){
return map.get(m) - map.get(n);
}
});
for(int key:map.keySet()){
queue.offer(key);
//当到数据k+1个时,依次删除堆顶,保持堆里面元素时k个
if(queue.size()>k){
queue.poll();
}
}
int[] res = new int[k];
for(int i=0;i<k;i++){
res[i] = queue.poll();
}
return res;
}
}
//hashmap + 手工建堆
class Solution {
//记录数组中元素及其对应出现的次数
Map<Integer , Integer> map = new HashMap<>();
public int[] topKFrequent(int[] nums, int k) {
int[] heap = new int[k];
for(int i : nums){
map.put(i , map.getOrDefault(i,0) + 1);
}
Iterator it = map.keySet().iterator();
int i = 0;
while(it.hasNext()){
// 先初始化一个大小为k的堆,当堆中元素个数为k时,建立小根堆
if( i < k){
heap[i] = (Integer)it.next();
i++;
if(i == k){
for(int j = k/2 -1 ; j >= 0 ; j--){
heapSort(heap,j);
}
}
}else{ //小根堆建好后 ,对于每个新遍历的元素与堆顶比较,如果比堆顶大,替换堆顶,重新维持小根堆
int key = (Integer)it.next();
if(map.get(key) > map.get(heap[0])) heap[0] = key;
heapSort(heap , 0);
}
}
return heap;
}
//维持小根堆
//注意,堆中元素比较是比较它们出现的次数,即该元素在map中对应的value
public void heapSort(int[] heap , int i){
int temp = heap[i];
for(int j = 2*i + 1 ; j < heap.length ; j = 2*j + 1){
if(j+1 < heap.length && map.get(heap[j+1]) < map.get(heap[j])) j++;
if(map.get(heap[j]) < map.get(temp)){
heap[i] = heap[j];
i = j;
}else break;
}
heap[i] = temp;
}
}
394. 字符串解码
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例 1:
输入:s = “3[a]2[bc]” 输出:“aaabcbc” 示例 2:
输入:s = “3[a2[c]]” 输出:“accaccacc”
来源:力扣(LeetCode)
class Solution {
int ptr;
public String decodeString(String s) {
s = s.trim();
if(s.length()==0||s==null) return s;
Deque<Integer> nums = new LinkedList<>();
Deque<String> strs = new LinkedList<>();
StringBuilder res = new StringBuilder();
int num = 0;
for(int i=0;i<s.length();i++){
//当当前字符是数字时候
if(s.charAt(i)>='0' && s.charAt(i)<='9') {
num = num *10 + s.charAt(i)-'0';
//当前字符是字符时,添加到可变字符中
}else if(s.charAt(i)>='a' && s.charAt(i)<='z' || s.charAt(i)>='A' && s.charAt(i)<='Z'){
res.append(s.charAt(i));
//将‘[’前的数字压入nums栈内, 字母字符串压入strs栈内
}else if(s.charAt(i)=='['){
nums.push(num);
strs.push(res.toString());
res = new StringBuilder();
num = 0;
//遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律
}else{
int time = nums.pop();
String tmp = strs.pop();
for(int j=0;j<time;j++){
tmp += res.toString();
}
//之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
//若是左括号,res会被压入strs栈,作为上一层的运算
res = new StringBuilder(tmp);
}
}
return res.toString();
}
}
除法求值
----待做