当前位置: 首页 > news >正文

基础算法(11)——栈

1. 删除字符串中的所有相邻重复项

题目描述

算法思路:

如果使用 stack 来保存的话,最后还需要把结果从栈中取出来,所以不如直接用【数组模拟一个栈】,结构:在数组的尾部【尾插尾删】,实现栈的【进栈】【出栈】,最后数组存留的内容就是最后的结果

代码实现:

class Solution {public String removeDuplicates(String s) {StringBuffer str = new StringBuffer();char[] chs = s.toCharArray();for (char ch : chs) {if (str.length() > 0 && ch == str.charAt(str.length() - 1)) {str.deleteCharAt(str.length() - 1);} else {str.append(ch);}}return str.toString();}
}

2. 比较含退格的字符串

题目描述

算法思路

 由于退格时需要知道【前面元素】的信息,而且退格也符合【后进先出】的特性,因此我们可以使用【栈】结构来模拟退格的过程

当遇到非 # 字符时,直接出栈

当遇到 # 字符时,栈顶元素出栈

为了方便,直接使用【数组】来模拟实现栈结构

代码实现:

class Solution {public boolean backspaceCompare(String s, String t) {return changeStr(s).equals(changeStr(t));}public String changeStr(String s) {StringBuffer ret = new StringBuffer();char[] tmp = s.toCharArray(); // 数组模拟栈结构for (int i = 0; i < tmp.length; i++) {if (tmp[i] != '#') {ret.append(tmp[i]); // 入栈} else {if (ret.length() > 0)ret.deleteCharAt(ret.length() - 1); // 出栈}}return ret.toString();}
}

3. 基本计算器II

题目描述

算法思路

由于表达式里面没有括号,因此我们只用处理【加减乘除】混合运算即可,根据四则运算的顺序,我们可以先计算乘除法,然后再计算加减法,由此得出以下结论:

  • 当一个数前面是 '+' 号时,这一个数是否会被立即计算是【不确定】的,因此我们可以先入栈
  • 当一个数前面是 '-' 号时,这一个数是否会被立即计算也是【不确定】的,并且这个数已经和前面的 '-' 号绑定了,所以我们可以将这个数的相反数入栈
  • 当一个数前面是 '*' 号时,这一个数可以立即与前面的一个数相乘,所以此时我们让栈顶元素出栈与之相乘,将结果入栈
  • 当一个数前面是 '/' 号时,这一个数可以立即与前面的一个数相乘,所以此时我们让栈顶元素出栈与之相除,将结果入栈

使用一个 int tmp 来暂存数字,这是为了应对数字不只一位的情况(如:123);使用栈来存放数字;使用 char op 来存放运算符,初始化为 '+'

当遍历完全部的表达式后,栈中剩余【元素之和】就是最终结果

代码实现

class Solution {public int calculate(String ss) {Deque<Integer> dq = new ArrayDeque<>();int n = ss.length();char[] ch = ss.toCharArray();int i = 0; // 用于遍历 chchar op = '+'; // 用于保存运算符while (i < n) {if (ch[i] == ' ') i++;else if (ch[i] >= '0' && ch[i] <= '9') {int tmp = 0;while (i < n && ch[i] >= '0' && ch[i] <= '9') {tmp *= 10;tmp += ch[i] - '0';i++;}if (op == '-') dq.push(-tmp);else if (op == '+') dq.push(tmp);else if (op == '*') dq.push(dq.pop() * tmp);else dq.push(dq.pop() / tmp);}else {op = ch[i];i++;}}int ret = 0;while (!dq.isEmpty()) {ret += dq.pop();}return ret; }
}

4. 字符串编码

题目描述

算法思路

题目要求解码 k[encoded_string] 格式的字符串,核心是嵌套处理重复逻辑(比如 3[a2[c]] 需先解内层 2[c] 再解外层 3[acc] )。下面用 两个栈 分工:

  • string 栈(代码里是 st)存待拼接的字符串片段(比如内层解码结果、普通字符段 )
  • int 栈(代码里是 nums)存重复次数 k

分情况讨论:

1. 初始化栈与变量
Stack<StringBuffer> st = new Stack<>();
st.push(new StringBuffer()); // 初始化放空串,作为最外层“容器”
Stack<Integer> nums = new Stack<>();
int n = s.length();
char[] ch = s.toCharArray();
int i = 0;
  • st.push(new StringBuffer()):提前放一个空 StringBuffer,后续所有解码结果都会拼接到这个基础容器里(比如示例 3[a]2[bc],最终结果会逐步拼到这个初始空串 )。
  • nums 栈:专门存解码需要的重复次数 k
  • ch 转数组 + i 指针:方便遍历字符串,逐个字符处理。
2. 遍历字符串:分情况处理逻辑

核心是 while (i < n) 里的 if-else,对应笔记里的四种场景:

① 遇到数字:提取完整的 k(处理连续数字,比如 12[abc] 里的 12
if (ch[i] >= '0' && ch[i] <= '9') {int tmp = 0;while (i < n && ch[i] >= '0' && ch[i] <= '9') {tmp = tmp * 10 + (ch[i] - '0'); // 拼接数字(如 '1','2' → 12)i++;}nums.push(tmp); // 存到数字栈,等后面遇到 ']' 时用
}
  • 逻辑:数字可能是多位数(如 100[abc]),所以用 while 循环 “吃” 完连续数字,转成整数 tmp 存入 nums 栈。
  • 关联笔记:对应 “遇到数字,提取放入数字栈”。
② 遇到 [:准备存新的字符串片段
else if (ch[i] == '[') {i++;StringBuffer tmp = new StringBuffer();while (i < n && ch[i] >= 'a' && ch[i] <= 'z') {tmp.append(ch[i]); // 提取 `[` 后的普通字符(如 `3[a2[c]]` 里 `[` 后可能先有 `a`)i++;}st.push(tmp); // 存到字符串栈,等后面遇到 ']' 时拼接
}
  • 逻辑:[ 是 “新片段开始” 的标记,先跳过 [i++),然后提取 [ 后面紧跟的普通字符(比如 2[bc] 里的 bc 会在这一步处理吗?不,这里处理的是 [ 后到 ] 前的 “非嵌套” 普通字符,嵌套的会在后续循环处理 ),存入 st 栈。
  • 关联笔记:对应 “遇到 [,提取后面字符串放入字符串栈”。
③ 遇到 ]:拼接重复片段(核心解码逻辑)
else if (ch[i] == ']') {StringBuffer tmp = st.pop(); // 取出待重复的片段(如 `2[c]` 里的 `c` 或内层解码后的 `acc`)int k = nums.pop(); // 取出重复次数(如 `2` 或 `3`)while (k-- != 0) {st.peek().append(tmp); // 拼接到栈顶的字符串(外层容器或上层片段)}i++;
}
  • 逻辑:] 是 “片段结束” 的标记,此时:
    • 从 st 弹出待重复的片段(比如 a2[c] 里,内层 ] 对应弹出 c,外层 ] 对应弹出 acc )。
    • 从 nums 弹出重复次数 k,循环 k 次把 tmp 拼接到栈顶剩余的字符串(因为 st 弹出了当前片段,栈顶是外层容器或上层未闭合的片段 )。
  • 关联笔记:对应 “遇到 ],解析(弹出片段 + 次数),拼到栈顶字符串后面”。
④ 遇到普通字符(非数字、非括号):直接拼接到栈顶
else {StringBuffer tmp = new StringBuffer();while (i < n && ch[i] >= 'a' && ch[i] <= 'z') {tmp.append(ch[i]); // 提取连续普通字符(如 `abc3[cd]` 里的 `abc`)i++;}st.peek().append(tmp); // 直接拼到当前栈顶的字符串
}
  • 逻辑:普通字符不需要重复,直接提取连续字母(比如 ef 或 xyz ),拼接到 st 栈顶的字符串里(因为栈顶始终是 “当前正在构建的片段” )。
  • 关联笔记:对应 “遇到单独字符,提取拼到栈顶字符串后面”。
  • 逻辑:整个解码过程中,所有片段最终都会拼接到最开始初始化的空 StringBuffer 里(因为每次 ] 拼接、普通字符拼接,都是往栈顶操作,而栈底初始空串会逐步累积结果 ),所以直接取栈顶(此时栈里只剩最终结果)转成字符串返回。
示例:

以示例 s = "3[a2[c]]" 为例,走一遍流程:

  1. 初始化:st = [""](栈底空串),nums = []i=0
  2. 遇到 '3':进入数字逻辑,提取 3 → nums = [3]i 移到 '[' 后(i=1)。
  3. 遇到 '[':进入 [ 逻辑,跳过 [i=2),提取 'a' → st = ["", "a"]i 移到 '2' 后(i=3)。
  4. 遇到 '2':提取 2 → nums = [3, 2]i 移到 '[' 后(i=4)。
  5. 遇到 '[':跳过 [i=5),提取 'c' → st = ["", "a", "c"]i 移到 ']' 后(i=6)。
  6. 遇到 ']':弹出 'c'tmp="c"),弹出 2k=2)→ 循环 2 次拼到栈顶 a → st = ["", "acc"]i=7
  7. 遇到 ']':弹出 'acc'tmp="acc"),弹出 3k=3)→ 循环 3 次拼到栈底空串 → st = ["accaccacc"]i=8(结束循环)。
  8. 返回 st.peek() → "accaccacc",与示例结果一致。

代码实现

class Solution {public String decodeString(String s) {// 1. 初始化栈与变量Stack<StringBuffer> st = new Stack<>();st.push(new StringBuffer()); // 初始化放空串,作为最外层“容器”Stack<Integer> nums = new Stack<>();int n = s.length();char[] ch = s.toCharArray();int i = 0;//2. 遍历字符串,分情况处理逻辑while (i < n) {// ①. 遇到数字:提取完整的 k(连续处理数字,比如 12[abc]中的 12)if (ch[i] >= '0' && ch[i] <= '9') {int tmp = 0;while (i < n && ch[i] >= '0' && ch[i] <= '9') {tmp = tmp * 10 + (ch[i] - '0'); // 拼接数字(如 '1','2' → 12)i++;}nums.push(tmp); // 存到数字栈,等后面遇到 ']' 时用} else if (ch[i] == '[') { // ②. 遇到 [:准备存新的字符串片段i++;StringBuffer tmp = new StringBuffer();while (i < n && ch[i] >= 'a' && ch[i] <= 'z') {tmp.append(ch[i]); // 提取 `[` 后的普通字符(如 `3[a2[c]]` 里 `[` 后可能先有 `a`)i++;}st.push(tmp); // 存到字符串栈,等后面遇到 ']' 时拼接} else if (ch[i] == ']') { // ③. 遇到 ]:拼接重复片段(核心逻辑)StringBuffer tmp = st.pop(); // 取出待重复的片段(如 `2[c]` 里的 `c` 或内层解码后的 `acc`)int k = nums.pop(); // 取出重复次数(如 `2` 或 `3`)while (k-- != 0) {st.peek().append(tmp); // 拼接到栈顶的字符串(外层容器或上层片段)}i++;} else { // ④. 遇到普通字符(非数字,非括号):直接拼接到栈顶StringBuffer tmp = new StringBuffer();while (i < n && ch[i] >= 'a' && ch[i] <= 'z') {tmp.append(ch[i]); // 提取连续普通字符(如 `abc3[cd]` 里的 `abc`)i++;}st.peek().append(tmp); // 直接拼到当前栈顶的字符串}}return st.peek().toString();}
}

5. 验证栈序列

题目描述

算法思路:

用栈来模拟进栈出栈的流程

一直让元素进栈,进栈的同时判断是否需要出栈,当所有元素模拟完毕之后,如果栈中还有元素,那么就是一个非法的序列,否则就是一个合法的序列

代码实现

import java.util.Stack;class Solution {public boolean validateStackSequences(int[] pushed, int[] popped) {Stack<Integer> stack = new Stack<>();int k = 0;for (int num : pushed) {stack.push(num);// 栈不为空且栈顶元素等于 popped 对应位置元素时,循环弹出并移动 popped 索引while (!stack.isEmpty() && stack.peek() == popped[k]) {stack.pop();k++;}}// 若 k 遍历完 popped 数组,说明匹配成功,否则失败return k == popped.length;}
}
http://www.dtcms.com/a/324559.html

相关文章:

  • 【3D图像技术分析与实现】CityGaussianV2 工作解析
  • log4cpp、log4cplus 与 log4cxx 三大 C++ 日志框架
  • 机器学习数学基础:46.Mann-Kendall 序贯检验(Sequential MK Test)
  • Java集合框架、Collection体系的单列集合
  • 有限元方法中的数值技术:追赶法求解三对角方程
  • 【鸿蒙/OpenHarmony/NDK】什么是NDK? 为啥要用NDK?
  • PCB知识07 地层与电源层
  • LLIC:基于自适应权重大感受野图像变换编码的学习图像压缩
  • 每日一题:使用栈实现逆波兰表达式求值
  • Redis高级
  • AAAI 2025丨具身智能+多模态感知如何精准锁定目标
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘ray’问题
  • Python数据分析常规步骤整理
  • Mysql系列--5、表的基本查询(下)
  • Speaking T2 - Dining Hall to CloseDuring Spring Break
  • 机器学习 DBScan
  • 一键复制产品信息到剪贴板
  • 【接口自动化】初识pytest,一文讲解pytest的安装,识别规则以及配置文件的使用
  • 网闸技术解析:如何实现对国产数据库(达梦/金仓)的深度支持
  • AI 代理框架:使用正确的工具构建更智能的系统
  • 网络小工具发布 IPPw
  • 机器学习之K-means(K-均值)算法
  • 七、CV_模型微调
  • SpringBoot学习日记(三)
  • P1152 欢乐的跳
  • 从零开始实现Qwen3(MOE架构)
  • C语言基础05——指针
  • Pinia 状态管理库
  • Redis - 使用 Redis HyperLogLog 进行高效基数统计
  • 无人机集群协同三维路径规划,采用梦境优化算法(DOA)实现,Matlab代码