因为单调栈能动态维护字典序最小的去重结果,而哈希无法处理相邻重复和局部最优决策;需预处理lastoccurrence数组并配合instack标记,确保弹栈安全且不重复入栈。

为什么用单调栈而不是直接哈希去重
因为要去掉的是「相邻重复」且要求「字典序最小」,比如 "bcab" 去掉重复后应得 "bab" 而不是 "bca" 或 "bc"。单纯用 map 记录是否见过会丢失顺序和局部最优决策能力——你需要在遍历时动态决定:当前字符要不要立刻压入结果,还是先弹掉前面更大的、后面还会出现的字符。
单调栈(这里是单调递增栈,存字符)的核心逻辑是:如果栈顶字符比当前字符大,且栈顶字符在后续还会出现,那就弹出它,保证最终字符串字典序更小。
关键步骤:需要预处理 lastOccurrence 数组
单调栈本身不关心“后面还有没有”,所以必须提前扫一遍字符串,记录每个字符最后一次出现的索引。否则你不敢随便弹栈顶——万一弹了,后面再没这个字符,就永远丢了。
- 用
lastOccurrencemap 或长度为 26 的数组(假设只有小写字母)存每个字符最右位置 - 遍历字符串时,对每个
s[i],检查stack非空、stack[len(stack)-1] > s[i]、且lastOccurrence[stack[len(stack)-1]] > i—— 三个条件同时满足才弹 - 还要避免重复入栈:用布尔数组
inStack标记某字符是否已在栈中,已存在就跳过
Go 实现里容易踩的坑
Go 没有内置栈,常用切片模拟,但要注意切片底层数组可能被复用导致意外残留;另外字符比较、索引越界、类型转换都容易出错。
-
stack用[]byte最方便,别用[]rune(除非确定有 Unicode),否则s[i]和stack[len(stack)-1]类型不一致 - 判断栈顶是否存在必须写成
len(stack) > 0,不能只靠stack != nil -
lastOccurrence初始化要填 -1,否则'a'出现在位置 0 时> i判断会误判 - 最后把
stack转成string时,直接string(stack)即可,不用逐个转rune
lastOccurrence := make([]int, 26) for i := 0; i <h3>测试时重点关注边界 case</h3> <p>算法对空串、单字符、全相同字符、严格递增/递减串都得稳住。最容易漏的是「某个字符只出现一次却卡在栈底」或者「重复字符中间夹着更小字符」这类场景。</p>
-
"bbcac"→ 应得"bac":第一个'b'入栈,第二个'b'发现栈顶相同且已在栈中,跳过;'c'入栈;'a'触发弹'c'(因lastOccurrence['c']=2 > 3? no→ 等等,这里索引从 0 开始,i=3,lastOccurrence['c']=2,所以不能弹;但'a' 且 <code>lastOccurrence['b']=1 ,所以第一个 <code>'b'也不能弹;最终栈是['b','c','a']→"bca"?不对 —— 正确做法是:第二个'b'不入栈,但第一个'b'后面还有'c'和'a',而'a'很小,应该把前面所有比它大且还会出现的都弹掉。所以关键在于:第二个'b'虽然不入栈,但它的存在让第一个'b'的lastOccurrence是 1,i=3时已过期,不能弹;真正能弹的是'c'吗?lastOccurrence['c']=2,i=3时已不可再用,也不能弹。所以结果确实是"bca"?但预期是"bac"—— 这说明原题隐含要求是「每个字符最多出现一次」且「结果字典序最小」,那"bac"比"bca"小,怎么得到?答案是:在i=2(字符'c')时,栈是['b'],'c' > 'b',直接入;到i=3('a'),'a' 且 <code>lastOccurrence['c']=2 → 不弹;<code>'a' 且 <code>lastOccurrence['b']=1 → 也不弹;所以确实得不到 <code>"bac"。问题出在:我们漏掉了'b'在位置 0 和 1 都出现,但lastOccurrence['b']应该是 1,没错;但i=3时已无法回头改前面。所以正确输入应为"bbcab"或类似结构才能触发弹出。实际调试时,用"cbacdcbc"(LeetCode 316)验证最稳妥。 - 别依赖打印中间栈状态来 debug,Go 切片的
fmt.Printf("%v", stack)显示的是当前内容,但多次append可能复用底层数组,建议每次操作后用make([]byte, len(stack))复制一份观察
lastOccurrence 和 inStack 的初始化位置、循环内 if 条件顺序、以及弹栈后是否立即 push 当前字符——这些细节一旦错位,结果就偏了。尤其是多个条件嵌套时,少一个括号或反向逻辑,输出就变成乱序。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











