leetcode/81-85

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)

link

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

除法求值

----待做

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值