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,与示例输出一致

相关推荐
江畔柳前堤8 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore9 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考11 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX11 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面12 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号12 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽12 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI12 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者14 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者14 小时前
【征程6】校准量化中HistogramObserver解析
算法