10.22总结

1636. 按照频率将数组升序排序

提示

简单

161

相关企业

给你一个整数数组 nums ,请你将数组按照每个值的频率 升序 排序。如果有多个值的频率相同,请你按照数值本身将它们 降序 排序。 

请你返回排序后的数组。

示例 1:

输入:nums = [1,1,2,2,2,3]
输出:[3,1,1,2,2,2]
解释:'3' 频率为 1,'1' 频率为 2,'2' 频率为 3 。

示例 2:

输入:nums = [2,3,1,3,2]
输出:[1,3,3,2,2]
解释:'2' 和 '3' 频率都为 2 ,所以它们之间按照数值本身降序排序。

示例 3:

输入:nums = [-1,1,-6,4,5,-6,1,4,1]
输出:[5,-1,4,4,-6,-6,1,1,1]

提示:

  • 1 <= nums.length <= 100
  • -100 <= nums[i] <= 100

 思路:

  1. 创建一个哈希表(HashMap)来统计数组中每个元素的频率,其中键是数组的元素,值是元素出现的次数。

  2. 创建一个自定义的比较器(Comparator)来排序数组,根据元素的频率进行排序。比较器应该首先比较两个元素的频率,如果频率相同,则比较它们的数值大小。

  3. 将数组传入排序算法,使用自定义的比较器进行排序。

  4. 返回排序后的数组。

class Solution {
    public int[] frequencySort(int[] nums) {
     Map<Integer,Integer> map=new HashMap();
     List<Integer> list=new ArrayList<>();
     for(int i=0;i<nums.length;i++)
     {
         if(!map.containsKey(nums[i])){
             map.put(nums[i],1);
             list.add(nums[i]);
         }
         else {
             map.put(nums[i],map.get(nums[i])+1);
             list.add(nums[i]);
         }
     }
    list.sort((a,b)->{
        int x=map.get(a);
        int y=map.get(b);
        if(x==y)
        return b-a;
        return x-y;
    });
    int[] res=new int[list.size()];
    for(int i=0;i<list.size();i++)
    {
        res[i]=list.get(i);
    }
    return res;
    }
}

994. 腐烂的橘子

中等

766

相关企业

在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:

  • 值 0 代表空单元格;
  • 值 1 代表新鲜橘子;
  • 值 2 代表腐烂的橘子。

每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。

返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

示例 1:

输入:grid = [[2,1,1],[1,1,0],[0,1,1]]
输出:4

示例 2:

输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个正向上。

示例 3:

输入:grid = [[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 10
  • grid[i][j] 仅为 01 或 2

思路:

这是一个典型的广度优先搜索(BFS)问题,你需要从腐烂的橘子开始,依次感染周围的新鲜橘子,直到无法感染为止。先初始化一个队列,将所有腐烂的橘子的位置放入队列,同时统计新鲜橘子的数量。然后,进行BFS遍历,依次感染周围的新鲜橘子,同时更新新鲜橘子的数量和分钟数。最后,返回分钟数,如果仍然有新鲜橘子没有被感染,则返回-1

class Solution {
       
    public int orangesRotting(int[][] grid) {
        int len = grid.length;
        int n = grid[0].length;
        int count = 0; 
        Queue<int[]> queue = new LinkedList<>();
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; 
        
        for (int i = 0; i < len; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 2) {
                    queue.offer(new int[]{i, j});
                } else if (grid[i][j] == 1) {
                    count++;
                }
            }
        }
        
        int minutes = 0;      
        while (!queue.isEmpty() && count > 0) {
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                int[] curr = queue.poll();
                int x = curr[0];
                int y = curr[1];
                
                for (int[] dir : directions) {
                    int newX = x + dir[0];
                    int newY = y + dir[1];
                    
                    if (newX >= 0 && newX < len && newY >= 0 && newY < n && grid[newX][newY] == 1) {
                        grid[newX][newY] = 2;
                        queue.offer(new int[]{newX, newY});
                        count--;
                    }
                }
            }
            minutes++;
        }
        
        return count == 0 ? minutes : -1;
    }
}

592. 分数加减运算

中等

142

相关企业

给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 

这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1

示例 1:

输入: expression = "-1/2+1/2"
输出: "0/1"

 示例 2:

输入: expression = "-1/2+1/2+1/3"
输出: "1/3"

示例 3:

输入: expression = "1/3-1/2"
输出: "-1/6"

提示:

  • 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/''+' 和 '-'。 
  • 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。
  • 输入只包含合法的最简分数,每个分数的分子分母的范围是  [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。
  • 输入的分数个数范围是 [1,10]。
  • 最终结果的分子与分母保证是 32 位整数范围内的有效整数。

思路:
 

  1. fractionAddition 方法用于解析分数加减运算表达式,如 "1/3+2/4-1/2",并返回最简分数的字符串表示形式。

  2. 初始化 result 为 "0/1",表示运算结果的初始分数,即0。

  3. 遍历表达式字符串 expression,使用指针 i 指向当前字符。

  4. 在循环中,从 i 开始查找下一个运算符(+ 或 -),并截取从 ij 之间的字符串,这个字符串表示一个分数,例如 "1/3"。

  5. 如果分数的前面没有运算符(即 i 处字符不是 + 或 -),则在分数前面加上 "+",以确保后续的 addFractions 方法能正确处理分数的加法。

  6. 调用 addFractions 方法,将 result 和当前分数相加,并更新 result

  7. 继续循环,将指针 i 移动到下一个运算符的位置,然后重复上述步骤,直到遍历完整个表达式。

  8. 最后返回 result,即最简分数的字符串表示形式。

  9. addFractions 方法用于将两个分数相加,首先解析分数字符串,然后执行分数加法操作。

  10. parse 方法用于将分数字符串解析成两个长整数,分别表示分子和分母。

  11. gcd 方法用于计算两个整数的最大公约数,以便最后将结果化简为最简分数。

class Solution {
    public String fractionAddition(String expression) {
        int len = expression.length();
        char[] ch = expression.toCharArray();
        String result = "0/1"; // 初始化结果为0
        int i = 0;

        while (i < len) {
            int j = i + 1;
            while (j < len && ch[j] != '+' && ch[j] != '-') {
                j++;
            }
            String num = expression.substring(i, j);
            
            if (ch[i] != '+' && ch[i] != '-') {
                num = "+" + num;
            }

            result = addFractions(result, num);
            i = j;
        }

        return result;
    }

    private String addFractions(String fraction1, String fraction2) {
        long[] frac1 = parse(fraction1);
        long[] frac2 = parse(fraction2);

        long numerator = frac1[0] * frac2[1] + frac1[1] * frac2[0];
        long denominator = frac1[1] * frac2[1];
        long gcd = gcd(Math.abs(numerator), Math.abs(denominator));

        return (numerator / gcd) + "/" + (denominator / gcd);
    }

    private long[] parse(String fraction) {
        String[] parts = fraction.split("/");
        long numerator = Long.parseLong(parts[0]);
        long denominator = Long.parseLong(parts[1]);
        return new long[] { numerator, denominator };
    }

    private long gcd(long a, long b) {
        while (b != 0) {
            long temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
}

1138. 字母板上的路径

提示

中等

121

相关企业

我们从一块字母板上的位置 (0, 0) 出发,该坐标对应的字符为 board[0][0]

在本题里,字母板为board = ["abcde", "fghij", "klmno", "pqrst", "uvwxy", "z"],如下所示。

我们可以按下面的指令规则行动:

  • 如果方格存在,'U' 意味着将我们的位置上移一行;
  • 如果方格存在,'D' 意味着将我们的位置下移一行;
  • 如果方格存在,'L' 意味着将我们的位置左移一列;
  • 如果方格存在,'R' 意味着将我们的位置右移一列;
  • '!' 会把在我们当前位置 (r, c) 的字符 board[r][c] 添加到答案中。

(注意,字母板上只存在有字母的位置。)

返回指令序列,用最小的行动次数让答案和目标 target 相同。你可以返回任何达成目标的路径。

示例 1:

输入:target = "leet"
输出:"DDR!UURRR!!DDD!"

示例 2:

输入:target = "code"
输出:"RR!DDRR!UUL!R!"

提示:

  • 1 <= target.length <= 100
  • target 仅含有小写英文字母。

思路:

 遍历目标字符串中的每个字符,计算当前字符在字母板上的目标位置。然后,通过比较当前位置和目标位置的行数和列数,使用 'U'、'D'、'L'、'R' 指令来移动到目标位置,以及 '!' 来添加当前字符到答案中。最终,返回答案的字符串表示形式。

class Solution {
    public String alphabetBoardPath(String target) {
        StringBuilder result = new StringBuilder();
        int row = 0, col = 0;

        for (char ch : target.toCharArray()) {
            int Row = (ch - 'a') / 5;
            int Col = (ch - 'a') % 5;

            while (row > Row) {
                result.append('U');
                row--;
            }

            while (col > Col) {
                result.append('L');
                col--;
            }

            while (row < Row) {
                result.append('D');
                row++;
            }

            while (col < Col) {
                result.append('R');
                col++;
            }

            result.append('!');
        }

        return result.toString();
    }
}

1997. 访问完所有房间的第一天

提示

中等

56

相关企业

你需要访问 n 个房间,房间从 0 到 n - 1 编号。同时,每一天都有一个日期编号,从 0 开始,依天数递增。你每天都会访问一个房间。

最开始的第 0 天,你访问 0 号房间。给你一个长度为 n 且 下标从 0 开始 的数组 nextVisit 。在接下来的几天中,你访问房间的 次序 将根据下面的 规则 决定:

  • 假设某一天,你访问 i 号房间。
  • 如果算上本次访问,访问 i 号房间的次数为 奇数 ,那么 第二天 需要访问 nextVisit[i] 所指定的房间,其中 0 <= nextVisit[i] <= i 。
  • 如果算上本次访问,访问 i 号房间的次数为 偶数 ,那么 第二天 需要访问 (i + 1) mod n 号房间。

请返回你访问完所有房间的第一天的日期编号。题目数据保证总是存在这样的一天。由于答案可能很大,返回对 109 + 7 取余后的结果。

示例 1:

输入:nextVisit = [0,0]
输出:2
解释:
- 第 0 天,你访问房间 0 。访问 0 号房间的总次数为 1 ,次数为奇数。
  下一天你需要访问房间的编号是 nextVisit[0] = 0
- 第 1 天,你访问房间 0 。访问 0 号房间的总次数为 2 ,次数为偶数。
  下一天你需要访问房间的编号是 (0 + 1) mod 2 = 1
- 第 2 天,你访问房间 1 。这是你第一次完成访问所有房间的那天。

示例 2:

输入:nextVisit = [0,0,2]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,0,0,1,2,...] 。
第 6 天是你访问完所有房间的第一天。

示例 3:

输入:nextVisit = [0,1,2,0]
输出:6
解释:
你每天访问房间的次序是 [0,0,1,1,2,2,3,...] 。
第 6 天是你访问完所有房间的第一天。

提示:

  • n == nextVisit.length
  • 2 <= n <= 105
  • 0 <= nextVisit[i] <= i

思路:

  1. 创建一个长度为 nflag 数组,用于记录每个房间的访问日期编号。初始时都为0。
  2. 遍历 nextVisit 数组,对于每个房间 i,计算 flag[i+1] 的值:
    • flag[i+1] 等于 (flag[i] + flag[i] - flag[nextVisit[i]] + i - nextVisit[i] + 1 + mod) % mod
    • 这个公式实际上是根据规则计算了第 i+1 个房间的访问日期编号,根据前一个房间的编号 flag[i]nextVisit[i],以及一些偏移和取模操作来计算。
  3. 返回 flag[n-1],表示访问完所有房间的第一天的日期编号。

这段代码的核心思想是通过动态规划的方式计算每个房间的访问日期,避免了显式模拟访问的过程。

class Solution {
    public int firstDayBeenInAllRooms(int[] nextVisit) {
     long[] flag=new long[nextVisit.length];
     int len=nextVisit.length;
     double mod=1e9+7;
     for(int i=0;i<len-1;i++)
     {
        flag[i+1]=(int)((flag[i]+flag[i]-flag[nextVisit[i]]+i-nextVisit[i]+1+mod)%mod);
     }
     return (int)((flag[len-1]+len-1)%mod);
 }
}

32. 最长有效括号

困难

2.4K

相关企业

给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

示例 1:

输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"

示例 2:

输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"

示例 3:

输入:s = ""
输出:0

提示:

  • 0 <= s.length <= 3 * 104
  • s[i] 为 '(' 或 ')'

思路:

  1. 遍历字符串,对于每个字符:
    • 如果是左括号 '(',将其索引位置入栈。
    • 如果是右括号 ')',检查栈是否为空。
      • 如果栈不为空,弹出栈顶的元素,并计算当前右括号的索引与栈顶元素之差,即当前有效括号子串的长度。更新 maxLen
      • 如果栈为空,说明当前右括号无法匹配,重置计数。
  2. 继续遍历字符串,但这次从右向左反向遍历,重复上述步骤,因为有些有效括号子串可能在右侧结束。

注意:在正向遍历和反向遍历的过程中,应该在栈为空时记录一个起始位置,用于计算长度 

class Solution {
  public int longestValidParentheses(String s) {
    int maxLen = 0;
    Stack<Integer> stack = new Stack<>();
    int start = -1; 

    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c == '(') {
            stack.push(i);
        } else {
            if (!stack.isEmpty()) {
                stack.pop();
                if (stack.isEmpty()) {
                    maxLen = Math.max(maxLen, i - start); 
                } else {
                    maxLen = Math.max(maxLen, i - stack.peek()); 
                }
            } else {
                start = i;
            }
        }
    }
    
    stack.clear(); 
    start = s.length(); 


    for (int i = s.length() - 1; i >= 0; i--) {
        char c = s.charAt(i);
        if (c == ')') {
            stack.push(i);
        } else {
            if (!stack.isEmpty()) {
                stack.pop();
                if (stack.isEmpty()) {
                    maxLen = Math.max(maxLen, start - i);
                } else {
                    maxLen = Math.max(maxLen, stack.peek() - i); 
                }
            } else {
                start = i; 
            }
        }
    }
    
    return maxLen;
}


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值