LeetCode刷题笔记(Java)---第681-700题

前言

需要开通vip的题目暂时跳过

笔记导航

点击链接可跳转到所有刷题笔记的导航链接

682. 棒球比赛

你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。

比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:

  1. 整数 x - 表示本回合新获得分数 x
  2. “+” - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
  3. “D” - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
  4. “C” - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
    请你返回记录中所有得分的总和。

在这里插入图片描述

  • 解答

    public int calPoints(String[] ops) {
          
          
            Stack<Integer> stack = new Stack<>();
            int res = 0;
            for(int i = 0;i < ops.length;i++){
          
          
                if(ops[i].equals("C")){
          
          
                    res -= stack.pop();
                }
                else if(ops[i].equals("D")){
          
          
                    Integer peek = stack.peek();
                    res += peek * 2;
                    stack.push(peek * 2);
                }
                else if(ops[i].equals("+")){
          
          
                    Integer pop = stack.pop();
                    Integer pop2 = stack.pop();
                    res += pop + pop2;
                    stack.push(pop2);
                    stack.push(pop);
                    stack.push(pop + pop2);
                }
                else {
          
          
                    int score = Integer.valueOf(ops[i]);
                    res += score;
                    stack.push(score);
                }
            }
            return res;
        }
    
  • 分析

    1. 栈来模拟得分的顺序
  • 提交结果
    在这里插入图片描述

684. 冗余连接

在本问题中, 树指的是一个连通且无环的无向图。

输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。

在这里插入图片描述

  • 解答

    public int[] findRedundantConnection(int[][] edges) {
          
          
            int nodesCount = edges.length;
            int[] parent = new int[nodesCount + 1];
            int[] sz = new int[nodesCount + 1];
            for (int i = 1; i <= nodesCount; i++) {
          
          
                parent[i] = i;
                sz[i] = i;
            }
            for (int i = 0; i < nodesCount; i++) {
          
          
                int[] edge = edges[i];
                int node1 = edge[0], node2 = edge[1];
                if (find(parent, node1) != find(parent, node2)) {
          
          
                    union(parent,sz, node1, node2);
                } else {
          
          
                    return edge;
                }
            }
            return new int[0];
        }
    
        public void union(int[] parent,int[] sz, int index1, int index2) {
          
          
            int p = find(parent,index1);
            int q = find(parent,index2);
            if(sz[p] > sz[q]){
          
          
                sz[p] += sz[q];
                parent[q] = p;
            }else{
          
          
                sz[q] += sz[p];
                parent[p] = q;
            }
        }
    
        public int find(int[] parent, int index) {
          
          
            if (parent[index] != index) {
          
          
                parent[index] = find(parent, parent[index]);
            }
            return parent[index];
        }
    
  • 分析

    1. 并查集
    2. 遍历边集合,维护并查集,当两个点的父亲相同,说明他们已经在树中出现了,则直接返回这条边
  • 提交结果
    在这里插入图片描述

686. 重复叠加字符串匹配

给定两个字符串 a 和 b,寻找重复叠加字符串 a 的最小次数,使得字符串 b 成为叠加后的字符串 a 的子串,如果不存在则返回 -1。

注意:字符串 “abc” 重复叠加 0 次是 “”,重复叠加 1 次是 “abc”,重复叠加 2 次是 “abcabc”。

在这里插入图片描述

  • 解答

    public int repeatedStringMatch(String a, String b) {
          
          
            if(a.contains(b)){
          
          
                return 1;
            }
            int min = b.length()/a.length();
            StringBuilder sb = new StringBuilder();
            for(int i = 1; i <= min+2; i++){
          
          
                sb.append(a);
                if(i>=min && sb.toString().contains(b)){
          
          
                    return i;
                }
            }
            return -1;
        }
    
  • 分析

    1. 思路:假设a为abc,b的为cabcabca,这时候b/a=2,就是最少要放两个a,可能a能包含b【此例子不成立,加上后面的条件才能得出答案,这一步只做粗筛选】

      • 如果刚好取的是完整的a字符串,那么,直接取b/a这个长度就可以了,例子:abc abcabc,此时取2;
      • 如果前面取的是不完整的a字符串,如abc cabcabc,这时候除完要加一个;同理,abc abcabca也要加一个,取3;
      • 如果前后都是不完整的a字符串,如abc cabcabca,则前后都加一个才能拿到b,此时取4;

      综上,只有b/a,b/a+1,b/a+2这三个数可能是正确答案,如果三个数都找不到,那就没有答案了。

  • 提交结果
    在这里插入图片描述

687. 最长同值路径

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

在这里插入图片描述

  • 解答

    int res = 0;
        public int longestUnivaluePath(TreeNode root) {
          
          
            dfs(root);
            return res;
        }
        Map<Integer,Integer> map = new HashMap<>();
        public int dfs(TreeNode root) {
          
          
            if(root == null)return 0;
            int left = 0,right = 0;
            if(root.left != null){
          
          
                if(root.val == root.left.val){
          
          
                    left += 1 + dfs(root.left);
                }else{
          
          
                    dfs(root.left);
                }
            }
            if(root.right != null){
          
          
                if(root.val == root.right.val){
          
          
                    right += 1 + dfs(root.right);
                }else{
          
          
                    dfs(root.right);
                }
            }
            res = Math.max(res,left + right);
            return Math.max(left,right);
        }
    
  • 分析

    1. dfs实现
    2. 递归判断同值路径的长度
    3. 若左孩子不为空,并且左孩子的值等于当前结点的值,那么左边路径 + 1 并递归的判断左子树
    4. 若右孩子不为空,并且右孩子的值等于当前结点的值,那么右边路径 + 1 并递归的判断右子树
    5. 当前结点的同值路径,就是左路径+右路径
    6. 返回给上一层结点的路径是左路径和右路径长度的较大者
  • 提交结果
    在这里插入图片描述

688. “马”在棋盘上的概率

已知一个 NxN 的国际象棋棋盘,棋盘的行号和列号都是从 0 开始。即最左上角的格子记为 (0, 0),最右下角的记为 (N-1, N-1)。

现有一个 “马”(也译作 “骑士”)位于 (r, c) ,并打算进行 K 次移动。

如下图所示,国际象棋的 “马” 每一步先沿水平或垂直方向移动 2 个格子,然后向与之相垂直的方向再移动 1 个格子,共有 8 个可选的位置。

在这里插入图片描述

现在 “马” 每一步都从可选的位置(包括棋盘外部的)中独立随机地选择一个进行移动,直到移动了 K 次或跳到了棋盘外面。

求移动结束后,“马” 仍留在棋盘上的概率。

在这里插入图片描述

  • 解答

    //方法1
    public double knightProbability(int N, int K, int rIndex, int cIndex) {
          
          
            int[][] f = new int[][]{
          
          {
          
          -2,1},{
          
          -1,2},{
          
          1,2},{
          
          2,1},{
          
          2,-1},{
          
          1,-2},{
          
          -1,-2},{
          
          -2,-1}};//8个方向
            double[][][] memo = new double[N][N][K + 1];
            return dfs(N,K,rIndex,cIndex,memo,f);
        }
        public double dfs(int N,int K,int rIndex,int cIndex,double[][][] memo,int[][] f){
          
          
            if(rIndex < 0 || rIndex >= N || cIndex < 0 || cIndex >= N)
                return 0;
            if(K==0)
                return 1;
            if(memo[rIndex][cIndex][K] != 0)return memo[rIndex][cIndex][K];
            double res = 0;
            for(int i = 0;i < 8;i++){
          
          
                res += dfs(N,K-1,rIndex + f[i][0],cIndex + f[i][1],memo,f);
            }
            memo[rIndex][cIndex][K] = res / 8;
            return res / 8;
        }
    //方法2
    public double knightProbability(int N, int K, int rIndex, int cIndex) {
          
          
            int[][] f = new int[][]{
          
          {
          
          -2,1},{
          
          -1,2},{
          
          1,2},{
          
          2,1},{
          
          2,-1},{
          
          1,-2},{
          
          -1,-2},{
          
          -2,-1}};//8个方向
            double[][][] dp = new double[N][N][K+1];
            for (int r = 0; r < N; r++) {
          
          
                for (int c = 0; c < N; c++) {
          
          
                    dp[r][c][0] = 1;
                }
            }
            for(int k = 1; k<= K;k++){
          
          
                for(int r = 0;r < N;r++){
          
          
                    for(int c = 0;c < N;c++){
          
          
                        for(int i = 0;i < 8;i++){
          
          
                            dp[r][c][k] += (r + f[i][0] >= 0 && r + f[i][0] < N && c + f[i][1] >= 0 && c + f[i][1] < N) ? dp[r + f[i][0]][c + f[i][1]][k - 1]: 0;
                        }
                        dp[r][c][k] /= 8;
                    }
                }
            }
            return dp[rIndex][cIndex][K];
        }
    
  • 分析

    1. 方法1 记忆化递归
    2. 递归出口,越界 返回0,K == 0 返回1,存在记忆 返回记忆
    3. dfs求8个方向存活的概率
    4. 记忆集记录 8个方向存活的概率之和 / 8
    5. 返回res/8
    6. 方法2 动态规划
    7. dp[i] [j] [k]表示 在位置i,j 走k步存活的概率
    8. 初始化 dp[i] [j] [0] = 1
    9. 然后遍历步数k等1 开始 到k
    10. 枚举所有的位置
    11. 动态转移方程为 dp[i] [j] [k] = 其他8个方向的k-1步的概率之和 除以8
    12. 最后返回dp[rIndex] [cIndex] [K]即可
  • 提交结果

    方法1在这里插入图片描述

    方法2在这里插入图片描述

689. 三个无重叠子数组的最大和

给定数组 nums 由正整数组成,找到三个互不重叠的子数组的最大和。

每个子数组的长度为k,我们要使这3*k个项的和最大化。

返回每个区间起始索引的列表(索引从 0 开始)。如果有多个结果,返回字典序最小的一个。

在这里插入图片描述

  • 解答

    public int[] maxSumOfThreeSubarrays(int[] nums, int k) {
          
          
            int L = nums.length;
            int[] sum = new int[nums.length+1];
    
            for(int i = 0; i < L; i++) {
          
          
                sum[i+1] = sum[i]+nums[i];
            }
    
            // value up to j-th element and with i sub-arrays
            int[][] dp = new int[4][L+1];//dp[i][j]表示 在j为止 有i组k大小的子数组和的最大值
            for(int i = 1; i < 4; i++) {
          
          
                for(int j = k; j <= L; j++) {
          
          
                    dp[i][j] = Math.max(dp[i][j-1], dp[i-1][j-k] + sum[j]-sum[j-k]);
                }
            }
    
            int[] ret = new int[3];
            int max = Arrays.stream(dp[3]).max().getAsInt();//3组子数组和的最大值
            for(int i = 3; i >= 1; i--) {
          
          //遍历子数组的个数 从3->1
                for(int j = 1; j <= L; j++) {
          
          //从前往后找,第一次出现的最大值
                    if(dp[i][j] == max){
          
          
                        ret[i-1] = j-k;//记录下最大值数组的起始位置
                        max -= sum[j]-sum[j-k];// 更新最大值,找下一个子数组和的最大值,例如刚才是找3组 现在是找2组的
                        break;
                    }
                }
            }
            return ret;
        }
    
  • 分析

    1. 动态规划+前缀和
    2. dp[i] [j]表示第j个元素结尾的数组中,有i组k大小的子数组 和的最大值
    3. 首先计算前缀和 保存在sum数组当中
    4. 然后动态转移,更新dp[i] [j]
    5. dp[i] [j] 有2种方式可以转移过来
      • dp[i] [j-1] 已经有i组k大小的子数组的最大值
      • dp[i-1] [j-k] + sum[j] - sum[j-k] 表示在j位置往前以k个元素结尾的地方,存在i-1组k大小的子数组和的最大值,那么 j-k ~ j的位置就是第i组子数组。
      • 取上面两者中的较大值
    6. 根据dp数组 得到3组k大小子数组的最大值
    7. 从前往后遍历,找到3组k大小子数组和的最大值最先出现的位置,此时遍历到的位置是j,那么这个子数组的起始的位置就是j-k。所以记录下j-k
    8. 然后就是要找2组k大小数组和的最大值,所以需要更新这个最大值,根据7中得到的第三组的位置,可以得到第三组的区间和,将最大值减去第三组的区间和。得到前两组子数组和的最大值。
    9. 接着从前往后遍历,找到2组k大小子数组和的最大值最先出现的位置,同上面的过程,记录下起始位置并更新最大值,再找第一组k大小子数组和的最大值,记录下起始位置,得到结果。
  • 提交结果
    在这里插入图片描述

690. 员工的重要性

给定一个保存员工信息的数据结构,它包含了员工唯一的id,重要度 和 直系下属的id。

比如,员工1是员工2的领导,员工2是员工3的领导。他们相应的重要度为15, 10, 5。那么员工1的数据结构是[1, 15, [2]],员工2的数据结构是[2, 10, [3]],员工3的数据结构是[3, 5, []]。注意虽然员工3也是员工1的一个下属,但是由于并不是直系下属,因此没有体现在员工1的数据结构中。

现在输入一个公司的所有员工信息,以及单个员工id,返回这个员工和他所有下属的重要度之和。

在这里插入图片描述

  • 解答

    		Map<Integer,Employee> map = new HashMap<>();
        public int getImportance(List<Employee> employees, int id) {
          
          
            int res = 0;
            for(Employee employee : employees){
          
          
                map.put(employee.id,employee);
            }
            for(Employee employee : employees){
          
          
                if(employee.id == id){
          
          
                    return dfs(employee);
                }
            }
            return 0;
        }
    
        public int dfs(Employee employee){
          
          
            int res =employee.importance;
            for(Integer i:employee.subordinates){
          
          
                res += dfs(map.get(i));
            }
            return res;
        }
    
  • 分析

    1. 先将所有的员工存入hashMap当中,id作为key,Employee作为value
    2. 遍历员工表,找到目标id员工
    3. 递归的遍历他底下的员工的信息,将importance累计
  • 提交结果在这里插入图片描述

691. 贴纸拼词

我们给出了 N 种不同类型的贴纸。每个贴纸上都有一个小写的英文单词。

你希望从自己的贴纸集合中裁剪单个字母并重新排列它们,从而拼写出给定的目标字符串 target。

如果你愿意的话,你可以不止一次地使用每一张贴纸,而且每一张贴纸的数量都是无限的。

拼出目标 target 所需的最小贴纸数量是多少?如果任务不可能,则返回 -1。

在这里插入图片描述

  • 解答

    public static int minStickers(String[] stickers, String target) {
          
          
            // 过滤判断:如果target中存在贴纸集中没有的字母,直接返回-1
            if (existLetterNotInStickers(stickers, target)) return -1;
    
            int[][] stickerMap = new int[stickers.length][26]; // stickerMap[i]:表示第i号贴纸stickers[i] 
            // 将贴纸集转换成字母频率表示,放入stickerMap
            for (int i = 0; i < stickers.length; i++) {
          
          
                for (char c : stickers[i].toCharArray()) {
          
          
                    stickerMap[i][c-'a']++;
                }
            }
            // 记忆化搜索 target -> 最少贴纸数
            HashMap<String, Integer> memo = new HashMap<>(); 
            return minStickers(stickerMap, target, memo);
        }
    
        // stickerMap:贴纸集
        // target:rest target,要搞定的剩余目标
        // 返回搞定target所需要的最少贴纸数
        private static int minStickers(int[][] stickerMap, String target, HashMap<String, Integer> memo) {
          
          
            if (memo.containsKey(target)) return memo.get(target);
    
            if (target.length() == 0) {
          
          
                memo.put("", 0);
                return 0;
            }
    
            char[] targetLetters = target.toCharArray(); // target
            int[] targetWord = new int[26]; // 以字母频率方式表示target
            for (char c : targetLetters) {
          
          
                targetWord[c-'a']++;
            }
            int minCount = Integer.MAX_VALUE;
    
            // 枚举第1张使用哪种贴纸
            for (int i = 0; i < stickerMap.length; i++) {
          
          
                int[] sticker = stickerMap[i]; // 决定使用i号贴纸
                // 能否使用这种贴纸?
                // 如果这种贴纸是"aaabbb",而target是"xyz",使用这种贴纸啥也搞定不了,递归下去就是死循环
                // 如果使用这种贴纸,target中至少得有一种字母在这种贴纸中,如果target中的字母在sticker中都不存在,搞定个锤子
                if (notContainAnyLetter(sticker, targetLetters)) continue;
                
                // 计算使用这张贴纸后,新的target是啥
                // 26种字母依次处理,得到每种字母处理后的字母频率表targetWord
                StringBuilder sb = new StringBuilder();
                for (int j = 0; j < 26; j++) {
          
          
                    // 如果目标中包含这种字母,使用1张贴纸后,计算这种字母还剩多少个
                    int restCount = targetWord[j];
                    if (targetWord[j] != 0) {
          
          
                        restCount = targetWord[j] > sticker[j] ? targetWord[j] - sticker[j] : 0;
                    }
                    // 将字母频率转成String:将这种字母添加到StringBuilder
                    for (int k = 0; k < restCount; k++) {
          
          
                        sb.append((char)(j+'a'));
                    }
                }
                String restTarget = sb.toString(); // 使用完第1张贴纸后,剩余目标
                int next = minStickers(stickerMap, restTarget, memo); // 【递归】后续需要的贴纸数量
                if (next != -1) minCount = Math.min(minCount, next + 1); // 后续过程有效,当前这种方案(使用i号贴纸作为第1张),是一种有效方案
            }
    
            int ans = minCount == Integer.MAX_VALUE ? -1 : minCount;
            memo.put(target, ans);
            return ans;
        }
    
        // 【贪心优化】
        // 如果target中的字母在sticker中都不存在,返回true
        // 如果target中至少存在一种字母在sticker中,返回false
        private static boolean notContainAnyLetter(int[] sticker, char[] target) {
          
          
            char toCheckLetter = target[0];
            return sticker[toCheckLetter-'a'] == 0;
        }
    
        // 判断target中是否含有贴纸集stickers中没有的字母,如果存在,返回true
        private static boolean existLetterNotInStickers(String[] stickers, String target) {
          
          
            int[] stickersLetters = new int[26];
            for (String sticker : stickers) {
          
          
                for (char letter : sticker.toCharArray()) {
          
          
                    stickersLetters[letter-'a']++;
                }
            }
            for (char letter : target.toCharArray()) {
          
          
                if (stickersLetters[letter-'a'] == 0) return true;
            }
            return false;
        }
    
  • 分析

    1. 记忆化递归
    2. 首先判断target当中是否存在某个字母 在stickers当中没有出现过,如果存在的话,直接返回-1
    3. 将stickers转换成字母数量统计表示
    4. 开始记忆化递归,记忆集用HashMap,字符串作为key,需要的贴纸的数量为value
    5. 用字母数量统计方式表示target
    6. 枚举贴纸,使用了这张贴纸后,得到剩余的目标,递归的计算,得到的结果放入记忆集。
  • 提交结果
    在这里插入图片描述

692. 前K个高频单词

给一非空的单词列表,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

在这里插入图片描述

  • 解答

    class Solution {
          
          
        public List<String> topKFrequent(String[] words, int k) {
          
          
            List<String> res = new ArrayList<>();
            Map<String,Integer> map = new HashMap<>();
            for(int i = 0;i < words.length;i++){
          
          
                map.put(words[i],map.getOrDefault(words[i],0)+1);
            }
            PriorityQueue<Letter> queue = new PriorityQueue<>(new Comparator<Letter>(){
          
          
                public int compare(Letter l1,Letter l2){
          
          
                    if(l1.number != l2.number){
          
          //频率不同,从大到小排
                        return l2.number - l1.number;
                    }
                    int minL = Math.min(l1.letter.length(),l2.letter.length());
                    for(int i = 0;i < minL;i++){
          
          //遍历
                        if(l1.letter.charAt(i) == l2.letter.charAt(i))continue;
                        return l1.letter.charAt(i) - l2.letter.charAt(i);//若字母不同,按字母顺序排序
                    }
                    return l1.letter.length() - l2.letter.length();//上面两个都相同的情况下,比较两个单词的长度。短的排前面
                }
            });
            for(String key:map.keySet()){
          
          
                queue.add(new Letter(key,map.get(key)));
            }
            while(k > 0){
          
          
                res.add(queue.poll().letter);
                k--;
            }
            return res;
        }
    }
    class Letter{
          
          
        String letter;
        int number;
        public Letter(String letter,int number){
          
          
            this.letter = letter;
            this.number = number;
        }
    }
    
  • 分析

    1. 利用堆来维护出现频率的高低以及字符顺序的排序。
    2. 首先用map,统计单词出现的频率
    3. 然后用优先级队列,模拟堆,来维护要求的排序。
    4. 最后取堆中前k个即可
  • 提交结果在这里插入图片描述

693. 交替位二进制数

给定一个正整数,检查它的二进制表示是否总是 0、1 交替出现:换句话说,就是二进制表示中相邻两位的数字永不相同。

在这里插入图片描述

  • 解答

    public boolean hasAlternatingBits(int n) {
          
          
            int last = n & 1;
            while(n >= 1){
          
          
                n = n >> 1;
                int cur = n & 1;
                if(cur == last)return false;
                last = cur;
            }
            return true;
        }
    
  • 分析

    1. 每次右移一位,然后与1 做与运算,就可以得到当前最后一位是1 还是0.然后和上一次的进行比较。
    2. 如果相同返回false;
  • 提交结果在这里插入图片描述

695. 岛屿的最大面积

给定一个包含了一些 0 和 1 的非空二维数组 grid 。

一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

在这里插入图片描述

  • 解答

    int[] f = {
          
          1,0,-1,0,1};
        public int maxAreaOfIsland(int[][] grid) {
          
          
            int res = 0;
            for(int i = 0;i < grid.length;i++){
          
          
                for(int j = 0;j < grid[0].length;j++){
          
          
                    if(grid[i][j] != 0)
                        res = Math.max(res,dfs(grid,i,j));
                }
            }
            return res;
        }
        public int dfs(int[][] grid,int r,int c){
          
          
            if(r < 0 || r >= grid.length || c < 0 || c >= grid[0].length || grid[r][c] == 0)return 0;
            int res =1
            grid[r][c] = 0;//清空,避免重复计算
            for(int i = 0;i < 4;i++){
          
          //4个方向
                res += dfs(grid,r + f[i],c + f[i+1]);
            }
            return res;
        }
    
  • 分析

    1. dfs 找连通的面积
    2. 枚举所有不为0的点,根据dfs 找到这个点连通的岛屿的面积,保留最大值
    3. 遍历过的点,设置为0,这样可以减少dfs的次数,避免重复的计算。
  • 提交结果在这里插入图片描述

696. 计数二进制子串

给定一个字符串 s,计算具有相同数量 0 和 1 的非空(连续)子字符串的数量,并且这些子字符串中的所有 0 和所有 1 都是连续的。

重复出现的子串要计算它们出现的次数。

在这里插入图片描述

  • 解答

    public int countBinarySubstrings(String s) {
          
          
            int zeroIndex = -1,zeroNumber = 0,oneIndex = -1,oneNumber = 0;
            char[] chars = s.toCharArray();
            int index = 0,res = 0;
            while(index < chars.length){
          
          
                if((chars[index] == '0' && oneIndex > zeroIndex) || (chars[index] == '1' && zeroIndex > oneIndex)){
          
          
                    res += Math.min(zeroNumber,oneNumber);
                }
                if(chars[index] == '0'){
          
          
                    if(zeroIndex == -1){
          
          
                        zeroIndex = index;
                        zeroNumber = 1;
                    }else{
          
          
                        if(zeroIndex > oneIndex){
          
          
                            zeroIndex++;
                            zeroNumber++;
                        }else{
          
          
                            zeroIndex = index;
                            zeroNumber = 1;
                        }
                    }
                }else{
          
          
                    if(oneIndex == -1){
          
          
                        oneIndex = index;
                        oneNumber = 1;
                    }else{
          
          
                        if(oneIndex > zeroIndex){
          
          
                            oneIndex++;
                            oneNumber++;
                        }else{
          
          
                            oneIndex = index;
                            oneNumber = 1;
                        }
                    }
                }
                index++;
            }
            res += Math.min(oneNumber,zeroNumber);
            return res;
        }
    
  • 分析

    1. 一次遍历,统计连续的1和连续的0的个数,当出现不连续的时候,则根据前面统计的1和0的数量取较小者,累计求和就是要找的目标数的个数。
    2. 例如 110001。遍历到第5个数字的时候,则根据前面统计的1和0的数量取较小者。11000 由2个1,3个0 所以 满足条件的数字是Math.min(2,3) = 2
    3. 遍历第五个数字,重新开始计算1的个数。
    4. 重复以上的过程。就可以得到结果。
  • 提交结果在这里插入图片描述

697. 数组的度

给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。

你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

在这里插入图片描述

  • 解答

    public int findShortestSubArray(int[] nums) {
          
          
            if(nums.length < 2)return nums.length;
            int[] counts = new int[50000];
            int[] first = new int[50000];
            int[] dis = new int[50000];
            int max = 0;
            Arrays.fill(first,-1);
            int maxNumber = 0;
            for(int i = 0;i < nums.length;i++){
          
          
                maxNumber = Math.max(maxNumber,nums[i]);
                counts[nums[i]]++;
                max = Math.max(max,counts[nums[i]]);
                if(first[nums[i]] == -1){
          
          
                    first[nums[i]] = i;
                }else{
          
          
                    dis[nums[i]] = i - first[nums[i]] + 1;
                }
            }
            int res = Integer.MAX_VALUE;
            for(int i = 0;i <= maxNumber;i++){
          
          
                if(counts[i] == max){
          
          
                    res = Math.min(res,dis[i]);
                }
            }
            return res == 0 ? 1 : res;
        }
    
  • 分析

    1. 准备3个数组
    2. counts用于统计数字出现的次数,first用于记录数字第一次出现的位置,dis用于记录某数字全部出现的最短子数组的长度。
    3. 一次遍历,维护上面三个数组,并记录下最大的数字
    4. 第二次遍历,0~最大数字
    5. 判断数字的个数满足max的最短的子数组的长度。
  • 提交结果在这里插入图片描述

699. 掉落的方块

在无限长的数轴(即 x 轴)上,我们根据给定的顺序放置对应的正方形方块。

第 i 个掉落的方块(positions[i] = (left, side_length))是正方形,其中 left 表示该方块最左边的点位置(positions[i][0]),side_length 表示该方块的边长(positions[i][1])。

每个方块的底部边缘平行于数轴(即 x 轴),并且从一个比目前所有的落地方块更高的高度掉落而下。在上一个方块结束掉落,并保持静止后,才开始掉落新方块。

方块的底边具有非常大的粘性,并将保持固定在它们所接触的任何长度表面上(无论是数轴还是其他方块)。邻接掉落的边不会过早地粘合在一起,因为只有底边才具有粘性。

返回一个堆叠高度列表 ans 。每一个堆叠高度 ans[i] 表示在通过 positions[0], positions[1], …, positions[i] 表示的方块掉落结束后,目前所有已经落稳的方块堆叠的最高高度。

在这里插入图片描述

  • 解答

    public List<Integer> fallingSquares(int[][] positions) {
          
          
            List<Integer> hList = new ArrayList<>();//存放当前方块的高度
            List<Integer> list = new ArrayList<>();//存放当前最大高度
            if(positions == null || positions.length == 0){
          
          
                return list;
            }
            int maxH = positions[0][1];
            int high = positions[0][1];
            hList.add(high);
            list.add(maxH);
            P:for(int i = 1; i < positions.length; i++){
          
          
                high = positions[i][1];
                //判断当前方块能否放在之前的方块上方,并取得最高值
                for(int j = i-1; j>=0; j--){
          
          //遍历已经放的块
                    if(!(positions[j][0] >= positions[i][0] + positions[i][1] || positions[j][0] +positions[j][1]<=  positions[i][0])){
          
          //若可以放在上面
                        int temp = hList.get(j) + positions[i][1];//高度叠加
                        if(temp > high){
          
          
                            high = temp;//更新高度
                        }
                    }
                }
                hList.add(high);//当前块的最终高度
                if(high > maxH){
          
          
                    maxH = high;
                }
                list.add(maxH);//存放当前的最大高度
            }
            return list;
        }
    
  • 分析

    1. 2个集合 一个hList用于存放每一块方块的最终真实高度,另一个list用于存放当前最大高度
    2. 一开始将第一块放入两个集合当中。
    3. 然后从第二块开始遍历
    4. 内层循环,遍历已经放置的方块。如果当前方块在已经放置的方块上面,那么就计算新的高度,取最大值。
    5. 内层循环结束可以得到方块的最终高度。
    6. 将当前块的最终高度 放入 hList集合当中,并更新当前所有块的最大高度。
  • 提交结果在这里插入图片描述

700. 二叉搜索树中的搜索

给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。

在这里插入图片描述

  • 解答

    public TreeNode searchBST(TreeNode root, int val) {
          
          
            if(root == null)return null;
            if(root.val == val)return root;
            if(root.val > val){
          
          
                return searchBST(root.left,val);
            }else return searchBST(root.right,val);
        }
    
  • 分析

  • 提交结果在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/gongsenlin341/article/details/112979699