Java实现LeetCode 题号:511 - 520

157 阅读2分钟

「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」。

LeetCode习题集 有些题可能直接略过了,整理一下之前刷leetcode

513. 找树左下角的值

给定一个二叉树,在树的最后一行找到最左边的值。

示例 1:

输入:

    2
   / \
  1   3

输出: 1

示例 2:

输入:

    1
   / \
  2   3
 /   / \
4   5   6
   /
  7

输出: 7

注意: 您可以假设树(即给定的根节点)不为 NULL。

PS:
    类似于BFS遍历

    如果有右节点,先添加右结点,在添加左节点,最后一个结点既是目标节点
    
    如果先添加左结点在添加右结点,最后求的是最后一行最右面的值。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
     public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            root = queue.poll();
            if (root.right != null) queue.offer(root.right);
            if (root.left != null) queue.offer(root.left);
        }
        return root.val;
    }
}

514. 自由之路

视频游戏“辐射4”中,任务“通向自由”要求玩家到达名为“Freedom Trail Ring”的金属表盘,并使用表盘拼写特定关键词才能开门。

给定一个字符串 ring,表示刻在外环上的编码;给定另一个字符串 key,表示需要拼写的关键词。您需要算出能够拼写关键词中所有字符的最少步数。

最初,ring 的第一个字符与12:00方向对齐。您需要顺时针或逆时针旋转 ring 以使 key 的一个字符在 12:00 方向对齐,然后按下中心按钮,以此逐个拼写完 key 中的所有字符。

旋转 ring 拼出 key 字符 key[i] 的阶段中:

您可以将 ring 顺时针或逆时针旋转一个位置,计为1步。旋转的最终目的是将字符串 ring 的一个字符与 12:00 方向对齐,并且这个字符必须等于字符 key[i] 。 如果字符 key[i] 已经对齐到12:00方向,您需要按下中心按钮进行拼写,这也将算作 1 步。按完之后,您可以开始拼写 key 的下一个字符(下一阶段), 直至完成所有拼写。 示例: 在这里插入图片描述

输入: ring = "godding", key = "gd" 输出: 4 解释: 对于 key 的第一个字符 'g',已经在正确的位置, 我们只需要1步来拼写这个字符。 对于 key 的第二个字符 'd',我们需要逆时针旋转 ring "godding" 2步使它变成 "ddinggo"。 当然, 我们还需要1步进行拼写。 因此最终的输出是 4。 提示:

ring 和 key 的字符串长度取值范围均为 1 至 100; 两个字符串中都只有小写字符,并且均可能存在重复字符; 字符串 key 一定可以由字符串 ring 旋转拼出。

Ps:
	DP【i】【j】指的是key第i个字符匹配的轮盘的第j个数字
	因为我是两边转,在dp基础上加一个判断正反最小的
        
        当 skey[i] == sring[j] 时
            如果是key第一个字符就直接计数,正着转或者反着转
            如果不是key第一个字符,就看上一个字符是到哪了,找到上一个字符转到这个字符最小的那个,这里也要考虑正反转
        
        如果是key最后一个字符,就记录一下,看看哪个是最少的

class Solution {
 

 public int findRotateSteps(String ring, String key) {
   
char[] sring = ring.toCharArray();
    char[] skey = key.toCharArray();
    int[][] dp = new int[key.length()][ring.length()];
     for(int i = 0 ; i < dp.length ; i ++){
        Arrays.fill(dp[i], Integer.MAX_VALUE);
    }
    int n = ring.length();
    int count = Integer.MAX_VALUE;
    for(int i = 0 ; i < skey.length ; i ++) {
        for(int j = 0 ; j < n; j ++){
            if(skey[i] == sring[j]){
                if(i == 0)
                    dp[i][j] = Math.min(j, n - j);
                else{
                    for(int k = 0 ; k < n ; k ++){
                        if(dp[i - 1][k] != Integer.MAX_VALUE){
                            dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + Math.min(Math.abs(j - k), n - Math.abs(j - k)));
                        }
                    }
                }
                
                if(i == skey.length - 1)
                    count = Math.min(count, dp[i][j]);
            }
        }
    }
    return count + skey.length;
}

515. 在每个树行中找最大值

您需要在二叉树的每一行中找到最大的值。

示例:

输入:

      1
     / \
    3   2
   / \   \  
  5   3   9 

输出: [1, 3, 9]

    PS:
        遍历树,递归遍历,遍历的同时加上对应的层数
        每遍历到一个节点,比较同层的结点,找到最大的那个值,保存最大的值
        list的对应的下标当作对应的层数,每个下标对应的值就是当前树对应层的最大值
        
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> list = new ArrayList<>();
    public List<Integer> largestValues(TreeNode root) {
        dfs(root,0);
        return list;
    }
    public void dfs(TreeNode tree,int cen){
        if(tree==null) return;
        if(cen==list.size()) list.add(tree.val);
        else{
            if(list.get(cen)<tree.val){
                list.remove(cen);
                list.add(cen,tree.val);
            }
        }
        dfs(tree.left,cen+1);
        dfs(tree.right,cen+1);
    }
}

516. 最长回文子序列

给定一个字符串s,找到其中最长的回文子序列。可以假设s的最大长度为1000。

示例 1: 输入:

"bbbab" 输出:

4 一个可能的最长回文子序列为 "bbbb"。

示例 2: 输入:

"cbbd" 输出:

2 一个可能的最长回文子序列为 "bb"。

PS:
动态规划,
第一个就不多说了,dp【i】【j】就是截取后i位,然后挨着截取后i位的第j位
			相等就+2,不相等找【i+1】【j】和【i】【j-1】中最大的
第二个,根据第一个我一直是用的我的上一个,因为我是i越来越小
	然后直接用两个数组,一个保存上一个,一个记录现在,
	然后替换即可
class Solution {
    //    public int longestPalindromeSubseq(String s) {
    //     if (s == null || s.length() == 0) {
    //         return 0;
    //     }
    //     int n = s.length();
    //     int[][] dp = new int[n][n];
    //     for (int i = n - 1; i >= 0; i--) {
    //         dp[i][i] = 1;
    //         for (int j = i + 1; j < n; j++) {
    //             if (s.charAt(i) == s.charAt(j)) {
    //                 dp[i][j] = dp[i + 1][j - 1] + 2;
    //             } else {
    //                 dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]);
    //             }
    //         }
    //     }
    //     return dp[0][n - 1];
    // }

    
    public int longestPalindromeSubseq(String s) {
        char[] chars=s.toCharArray();
        int length=s.length();
        int[] current=new int[length];
        int[] pre =new int[length]; 
        for(int i=length-1;i>=0;i--){
            current[i]=1;
            for(int j=i+1;j<length;j++){
                if(chars[i]==chars[j]){
                    current[j]=pre[j-1]+2;
                }else{
                    current[j]=Math.max(current[j-1],pre[j]);
                }
            }
            int[] tmp=pre;
            pre=current;
            current=tmp;
        }
        return pre[length-1];
    }
    
}

517. 超级洗衣机

假设有 n 台超级洗衣机放在同一排上。开始的时候,每台洗衣机内可能有一定量的衣服,也可能是空的。

在每一步操作中,你可以选择任意 m (1 ≤ m ≤ n) 台洗衣机,与此同时将每台洗衣机的一件衣服送到相邻的一台洗衣机。

给定一个非负整数数组代表从左至右每台洗衣机中的衣物数量,请给出能让所有洗衣机中剩下的衣物的数量相等的最少的操作步数。如果不能使每台洗衣机中衣物的数量相等,则返回 -1。

示例 1:

输入: [1,0,5]

输出: 3

解释:

第一步:    1     0 <-- 5    =>    1     1     4
第二步:    1 <-- 1 <-- 4    =>    2     1     3    
第三步:    2     1 <-- 3    =>    2     2     2   

示例 2:

输入: [0,3,0]

输出: 2

解释: 
第一步:    0 <-- 3     0    =>    1     2     0    
第二步:    1     2 --> 0    =>    1     1     1     

示例 3:

输入: [0,2,0]

输出: -1

解释: 不可能让所有三个洗衣机同时剩下相同数量的衣物。

提示:

n 的范围是 [1, 10000]。 在每台超级洗衣机中,衣物数量的范围是 [0, 1e5]。

PS:
	我移动的最小的次数,无非来源于两种
		我当前值和平均值的差
		我前面差的和
class Solution {
    public int findMinMoves(int[] machines) {
        int sum = 0;
        for(int num : machines)
          sum += num;
        if(sum % machines.length != 0)
            return -1;

        int target = sum / machines.length;
        int res = 0, balance = 0;
        for(int i = 0 ; i < machines.length; i ++){
            balance += machines[i] - target;
            res = Math.max(res, Math.max(machines[i] - target, Math.abs(balance)));
        }
        return res;
    }
}

518. 零钱兑换 II

给定不同面额的硬币和一个总金额。写出函数来计算可以凑成总金额的硬币组合数。假设每一种面额的硬币有无限个。

示例 1:

输入: amount = 5, coins = [1, 2, 5] 输出: 4 解释: 有四种方式可以凑成总金额: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1 示例 2:

输入: amount = 3, coins = [2] 输出: 0 解释: 只用面额2的硬币不能凑成总金额3。 示例 3:

输入: amount = 10, coins = [10] 输出: 1

注意:

你可以假设:

0 <= amount (总金额) <= 5000 1 <= coin (硬币面额) <= 5000 硬币种类不超过 500 种 结果符合 32 位符号整数

PS:
	当前的钱在大于硬币面值得情况下就是我的【j-当前硬币得面值】
        把每一种金币都循环一遍
class Solution {
    public int change(int amount, int[] coins) {
        int dp[] = new int[amount+1]; 
        dp[0] = 1;
        
        for (int coin : coins) { 
            for (int j = 1; j <= amount; j++) {
                if (j >= coin) { 
                    dp[j] = dp[j] + dp[j - coin];
                }
            }
        }
        return dp[amount];
    }
}

519. 随机翻转矩阵

题中给出一个 n 行 n 列的二维矩阵 (n_rows,n_cols),且所有值被初始化为 0。要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位置下标 [row_id,col_id];同样编写一个 reset 函数,将所有的值都重新置为 0。尽量最少调用随机函数 Math.random(),并且优化时间和空间复杂度。

注意:

1.1 <= n_rows, n_cols <= 10000

  1. 0 <= row.id < n_rows 并且 0 <= col.id < n_cols

3.当矩阵中没有值为 0 时,不可以调用 flip 函数

4.调用 flip 和 reset 函数的次数加起来不会超过 1000 次

示例 1:

输入: ["Solution","flip","flip","flip","flip"] [[2,3],[],[],[],[]] 输出: [null,[0,1],[1,2],[1,0],[1,1]] 示例 2:

输入: ["Solution","flip","flip","reset","flip"] [[1,2],[],[],[],[]] 输出: [null,[0,0],[0,1],null,[0,0]] 输入语法解释:

输入包含两个列表:被调用的子程序和他们的参数。Solution 的构造函数有两个参数,分别为 n_rows 和 n_cols。flip 和 reset 没有参数,参数总会以列表形式给出,哪怕该列表为空

PS: 自己映射一个数组 map保存点的信息,点采用横坐标乘纵坐标的方法存储 ,reset时就重置该点

class Solution {

    
    Map<Integer, Integer> V = new HashMap<>();
    int nr, nc, rem;
    Random rand = new Random();

    public Solution(int n_rows, int n_cols) {
        nr = n_rows;
        nc = n_cols;
        rem = nr * nc;
    }

    public int[] flip() {
        int r = rand.nextInt(rem--);
        int x = V.getOrDefault(r, r);
        V.put(r, V.getOrDefault(rem, rem));
        return new int[]{x / nc, x % nc};
    }

    public void reset() {
        V.clear();
        rem = nr * nc;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(n_rows, n_cols);
 * int[] param_1 = obj.flip();
 * obj.reset();
 */

520. 检测大写字母

给定一个单词,你需要判断单词的大写使用是否正确。

我们定义,在以下情况时,单词的大写用法是正确的:

全部字母都是大写,比如"USA"。 单词中所有字母都不是大写,比如"leetcode"。 如果单词不只含有一个字母,只有首字母大写, 比如 "Google"。 否则,我们定义这个单词没有正确使用大写字母。

示例 1:

输入: "USA" 输出: True 示例 2:

输入: "FlaG" 输出: False 注意: 输入是由大写和小写拉丁字母组成的非空单词。

class Solution {
    public boolean detectCapitalUse(String word) {
            int len = word.length();
            int cap = 0;
            for(int i=0; i<len; i++){
                char c = word.charAt(i);
                if(c >= 'A' && c <= 'Z') cap++;
            }
                //判断全是大写和全是小写的情况
            if(cap == len || cap == 0) return true;
            char f = word.charAt(0);
                //判断第一个是大写,其他都是小写的情况
            if(cap == 1 && (f >= 'A') && (f <= 'Z')) return true;
            return false;
    }
}