LeetCode 热题 100 之 394. 字符串解码 739. 每日温度 84. 柱状图中的最大矩形

394. 字符串解码

复制代码
class Solution {
    public String decodeString(String s) {
        Deque<Integer> countStack = new LinkedList<>();
        Deque<String> strStack = new LinkedList<>();
        StringBuilder res = new StringBuilder();
        int multi = 0;
        
        for (char c : s.toCharArray()) {
            if (c >= '0' && c <= '9') {
                multi = multi * 10 + (c - '0');
            } else if (c == '[') {
                countStack.push(multi);
                strStack.push(res.toString());
                multi = 0;
                res = new StringBuilder();
            } else if (c == ']') {
                int repeat = countStack.pop();
                String pre = strStack.pop();
                // 核心逻辑:先保留原来的结果,然后循环追加 repeat 次
                StringBuilder temp = new StringBuilder(pre);
                for (int i = 0; i < repeat; i++) {
                    temp.append(res);
                }
                res = temp;
            } else {
                res.append(c);
            }
        }
        return res.toString();
    }
}
解题思路1:栈

使用两个栈 保存「之前的状态」,遇到 ] 时触发解码:

  1. 数字栈(countStack) :保存遇到 [ 时的重复次数 k。

  2. 字符串栈(strStack) :保存遇到 [ 时的当前结果字符串 (即 [ 之前的部分)。

  3. 变量

    1. currentStr:记录当前正在拼接的字符串。

    2. currentNum:记录当前累积的数字(处理多位数)。

操作流程

  • 数字 (0-9) :累积到 currentNum(例如 123currentNum = 123)。

  • 左括号 [ :将当前的 currentNumcurrentStr 分别压入对应栈,然后重置 currentNumcurrentStr(开始记录括号内的内容)。

  • 右括号 ] :弹出数字栈顶 k 和字符串栈顶 prevStr。将 currentStr 重复 k 次,拼接结果为 prevStr + currentStr * k,更新给 currentStr

  • 字母 :直接拼接到 currentStr 末尾。

    class Solution {
    String src; // 保存原始字符串(全局,避免递归传参)
    int ptr; // 全局遍历指针,记录当前解析位置

    复制代码
      public String decodeString(String s) {
          src = s;
          ptr = 0;
          return getString(); // 启动递归解析
      }
    
      // 核心递归函数:解析当前层级的字符串
      public String getString() {
          // 终止条件:指针越界 或 遇到右括号(当前层级解析完成)
          if (ptr == src.length() || src.charAt(ptr) == ']') {
              return "";
          }
    
          char cur = src.charAt(ptr);
          int repTime = 1; // 默认重复次数为1(字母场景)
          String ret = ""; // 当前层级的结果
    
          if (Character.isDigit(cur)) {
              // 情况1:遇到数字 → 解析 "数字[String]String" 结构
              repTime = getDigits(); // 解析连续数字(如123→123)
              ++ptr; // 跳过左括号 '['
              String str = getString(); // 递归解析括号内的字符串(子问题)
              ++ptr; // 跳过右括号 ']'
              // 重复拼接:把括号内的字符串重复 repTime 次
              while (repTime-- > 0) {
                  ret += str;
              }
          } else if (Character.isLetter(cur)) {
              // 情况2:遇到字母 → 解析 "字母String" 结构
              ret = String.valueOf(src.charAt(ptr++)); // 取当前字母,指针后移
          }
          
          // 关键:拼接当前结果 + 递归解析后续字符串(处理 "XXXYYY" 连续场景)
          return ret + getString();
      }
    
      // 辅助函数:解析连续的数字(处理多位数,如 "100[abc]")
      public int getDigits() {
          int ret = 0;
          while (ptr < src.length() && Character.isDigit(src.charAt(ptr))) {
              // 累积数字:ret = ret*10 + 新数字(字符转数字:c-'0')
              ret = ret * 10 + src.charAt(ptr++) - '0';
          }
          return ret;
      }

    }

解题思路2:递归

这个解法的核心是递归 分解问题,把复杂的嵌套解码拆成更小的子问题:

  • 用全局 / 成员变量 src 保存原始字符串,ptr 作为全局遍历指针(避免递归传参,效率更高);

  • 递归函数 getString():负责解析「当前层级」的字符串,遇到 ] 或字符串结束时返回;

  • 辅助函数 getDigits():解析连续的数字(处理多位数)。

递归 语法分析(关键)

官方解法隐含了「上下文无关文法」的思路,把解码规则拆分为 3 种情况:

  1. String → 空串(遇到 ] 或字符串结束);

  2. String → 数字 [ String ] String(嵌套解码);

  3. String → 字母 String(普通字母拼接)。

739. 每日温度

复制代码
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int n = temperatures.length;
        int[] answer = new int[n];
        // 单调栈:存储未找到更高温度的日期索引,保证温度单调递减
        Deque<Integer> stack = new LinkedList<>();
        
        for (int i = 0; i < n; i++) {
            // 当前温度 > 栈顶温度 → 栈顶找到了下一个更高温度
            while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                int top = stack.pop();
                answer[top] = i - top; // 天数差 = 当前索引 - 栈顶索引
            }
            // 当前温度 ≤ 栈顶温度 → 入栈等待后续匹配
            stack.push(i);
        }
        return answer;
    }
}
解题思路1:单调

我们维护一个单调递减栈 ,栈中存储的是未找到更高温度的日期索引 ,保证栈内温度从栈底到栈顶单调递减

遍历规则(从左到右):

  1. 对于当前温度 temperatures[i]

    1. 若当前温度 > 栈顶温度:

      • 说明当前温度是栈顶日期的「下一个更高温度」,弹出栈顶索引 top,计算 i - top 并填入 answer[top]

      • 重复此过程,直到栈为空或当前温度 ≤ 栈顶温度。

    2. 若当前温度 ≤ 栈顶温度:将当前索引 i 压入栈。

  2. 遍历结束后,栈中剩余的索引代表「之后无更高温度」,answer 中对应位置保持 0

    // 暴力解法(O(n²),n=1e5 时会超时)
    class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
    int n = temperatures.length;
    int[] answer = new int[n];
    for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
    if (temperatures[j] > temperatures[i]) {
    answer[i] = j - i;
    break;
    }
    }
    }
    return answer;
    }
    }
    //这个不超时
    class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
    int length = temperatures.length;
    int[] ans = new int[length];
    int[] next = new int[101];
    Arrays.fill(next, Integer.MAX_VALUE);
    for (int i = length - 1; i >= 0; --i) {
    int warmerIndex = Integer.MAX_VALUE;
    for (int t = temperatures[i] + 1; t <= 100; ++t) {
    if (next[t] < warmerIndex) {
    warmerIndex = next[t];
    }
    }
    if (warmerIndex < Integer.MAX_VALUE) {
    ans[i] = warmerIndex - i;
    }
    next[temperatures[i]] = i;
    }
    return ans;
    }
    }

解题思路2:暴力(会超时)

第二个暴力关键点

  1. 不是纯暴力:该解法的内层循环次数是固定的 100 次(温度范围),而非和数组长度 n 相关,因此时间复杂度是 O (n),不会超时;

  2. 空间换时间 :用长度为 101 的 next 数组记录每个温度最近出现的索引,避免了重复遍历;

  3. 局限性:该解法仅适用于「值域有限且较小」的场景(如本题温度 1~100),若值域扩大到 1~1e5,内层循环 1e5 次会导致 O (n*1e5) = 1e10 次操作,反而超时

84. 柱状图中的最大矩形

复制代码
class Solution {
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int maxArea = 0;
        // 单调递增栈:存储柱子索引,保证对应高度单调递增
        Deque<Integer> stack = new LinkedList<>();
        
        for (int i = 0; i <= n; i++) {
            // 最后遍历到 i = n,高度视为 0,用于清空栈中剩余元素
            int currentHeight = (i == n) ? 0 : heights[i];
            // 当前高度 < 栈顶高度 → 弹出栈顶计算面积
            while (!stack.isEmpty() && currentHeight < heights[stack.peek()]) {
                int top = stack.pop();
                int height = heights[top];
                // 左边界:新栈顶索引(栈空则为 -1)
                int left = stack.isEmpty() ? -1 : stack.peek();
                // 右边界:当前索引 i
                int width = i - left - 1;
                maxArea = Math.max(maxArea, height * width);
            }
            stack.push(i);
        }
        return maxArea;
    }
}
解题思路:单调

维护一个单调递增栈 ,栈中存储柱子的索引,保证栈内对应的高度单调递增

  1. 遍历每个柱子 i

    1. 若当前高度 heights[i] < 栈顶高度:

      • 弹出栈顶索引 top,以 heights[top] 为高。

      • 左边界:新栈顶索引(若栈空则为 -1)。

      • 右边界:当前索引 i

      • 宽度 = i - left - 1,计算面积并更新最大值。(i是边界,面积暂时不参与计算)

      • 重复此过程,直到栈为空或当前高度 ≥ 栈顶高度。

    2. 若当前高度 ≥ 栈顶高度:将 i 压入栈。

  2. 遍历结束后,栈中剩余元素需要继续处理(右边界为数组长度 n)。

示例执行流程(以示例 1: [2,1,5,6,2,3] 为例)

|---|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------|------|---------|
| i | currentHeight | 栈操作 | 面积计算 | maxArea |
| 0 | 2 | 入栈 | - | 0 |
| 1 | 1 | 1<2 → 弹出 0:height=2, left=-1, width=1 → area=2 | 2 | |
| 1 | 1 | 入栈 | - | 2 |
| 2 | 5 | 5>1 → 入栈 | - | 2 |
| 3 | 6 | 6>5 → 入栈 | - | 2 |
| 4 | 2 | 2<6 → 弹出 3:height=6, left=2, width=1 → area=62<5 → 弹出 2:height=5, left=1, width=2 → area=10 | 10 | |
| 4 | 2 | 2>1 → 入栈 | - | 10 |
| 5 | 3 | 3>2 → 入栈 | - | 10 |
| 6 | 0 | 0<3 → 弹出 5:height=3, left=4, width=1 → area=3 0<2 → 弹出 4:height=2, left=1, width=4 → area=8 0<1 → 弹出 1:height=1, left=-1, width=6 → area=6 | 10 | |

最终 maxArea = 10,与示例输出一致

相关推荐
重生之后端学习2 小时前
62. 不同路径
开发语言·数据结构·算法·leetcode·职场和发展·深度优先
小资同学2 小时前
考研机试 -Kruskal算法
算法
y芋泥啵啵gfe2 小时前
AI考研深造VS直接工作:选对赛道,认证为竞争力加码
人工智能·职场和发展
big_rabbit05022 小时前
[算法][力扣283]Move Zeros
算法·leetcode·职场和发展
小资同学2 小时前
考研机试动态规划 线性DP
算法·动态规划
listhi5202 小时前
两台三相逆变器并联功率分配控制MATLAB实现
算法
Evand J2 小时前
【IMM】非线性目标跟踪算法与MATLAB实现:基于粒子滤波的交互式多模型,结合CV和CT双模型对三维空间中的机动目标进行高精度跟踪
算法·matlab·目标跟踪·pf·粒子滤波·imm·多模型
重生之后端学习2 小时前
64. 最小路径和
数据结构·算法·leetcode·排序算法·深度优先·图论
We་ct3 小时前
LeetCode 212. 单词搜索 II:Trie+DFS 高效解法
开发语言·算法·leetcode·typescript·深度优先·图搜索算法·图搜索