LeetCode-Book 精选题解:栈与哈希表 O(1) 匹配,秒解「有效的括号」
2026/9/16 16:24:45 网站建设 项目流程

LeetCode-Book 精选题解:栈与哈希表 O(1) 匹配,秒解「有效的括号」

【免费下载链接】LeetCode-Book《剑指 Offer》《图解算法数据结构》《Krahets 笔面试精选 88 题》Python, Java, C++ 解题代码项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

导读

「有效的括号」是《Krahets 笔面试精选 88 题》中考察**栈(Stack)**这一基础数据结构的入门级高频题:给定一个只包含'('')''{''}''['']'的字符串,判断括号的组成是否有效。本文以 selected_coding_interview/docs/20. 有效的括号.md 为主体,完整讲解「栈 + 哈希表」解法背后的算法原理、逐步执行流程、边界条件处理技巧与复杂度分析,并结合仓库中 Python 实现 与 Java 实现 给出可直接运行的代码。读完本文,你将掌握如何利用栈的"先入后出"特性处理配对类问题,并学会用哨兵元素优雅规避空栈异常这一经典技巧。

题目速览与整体思路

题目要求判断括号字符串是否"有效",有效定义通常为:左括号必须用相同类型的右括号闭合,且必须以正确的顺序闭合(允许嵌套,如"{[]}"有效,而"([)]"无效)。

核心观察:括号的"后出现的左括号先被匹配"特性与栈的**先入后出(LIFO)**结构完全吻合——遇到左括号入栈,遇到右括号时弹出栈顶左括号并检查两者是否配对。若遍历完整个字符串后栈恰好为空,则说明所有括号都正确闭合。

算法原理:栈的配对特性 + 哈希表 O(1) 查询

  • :栈先入后出的特点与本题括号排序特点一致。若遇到左括号就入栈,遇到右括号时将对应栈顶左括号出栈,则遍历完所有括号后stack仍然为空,即可判定为有效;
  • 哈希表:建立哈希表dic构建左右括号的对应关系,其中key为左括号、value为右括号。这样查询两个括号是否对应只需O(1)时间复杂度,无需在每次匹配时使用条件分支逐一比较,代码也更简洁、更易扩展(如需新增括号类型,只需在哈希表中增加一条映射)。

算法流程:逐字符遍历与即时判定

按照原文档给出的流程,遍历字符串s的每个字符c

  1. 如果c是左括号,则将其入栈(push)
  2. 否则(c是右括号),通过哈希表判断括号对应关系:若栈顶出栈的括号stack.pop()与当前遍历的右括号c不对应,则说明括号序列非法,提前返回false
  3. 遍历结束后,若栈中只剩下初始的哨兵元素,则返回true

提前返回 false 与边界条件处理

提前返回的优点:在迭代过程中提前发现不符合要求的括号并返回,可以省去后续无谓的遍历,提升算法效率。例如s = "()[]}"在遍历到最后一个字符时立刻返回false,无需再做任何额外检查。

但"边遍历边弹出"的设计会引入两个必须处理的边界情况,这也是本题最容易写错的地方:

边界一:栈为空时遇到右括号

s以右括号开头(如")"),此时栈是空的,直接执行stack.pop()会抛出异常/报错。原文档给出了一个取巧方法

  • stack赋初值'?'(哨兵元素);
  • 同时在哈希表dic中建立key: '?'value: '?'的对应关系予以配合。

这样当栈为空且c为右括号时,弹出的'?'与任何真实右括号都不匹配(dic['?'] = '?' != c),从而可以正常地提前返回false,完全避开空栈异常。哨兵'?'只需保证"不在题目给定的合法字符集内"即可,'?''#'等任意占位符都可胜任。

边界二:字符串以左括号结尾

s以左括号结尾(如"(()"),整个字符串可以正常遍历完毕,但栈中会遗留未出栈的左括号。此时直接返回true显然是错误的。因此,遍历结束后不能判断stack是否为空,而要判断len(stack) == 1(即是否只剩初始哨兵),从而正确识别"存在未闭合左括号"的非法序列。

这两种边界处理在 Python 源码 与 Java 源码 中均有完整体现。

复杂度分析

  • 时间复杂度 O(N):正确的括号组合需要完整遍历一遍s,其中哈希表查询为 O(1),整体为 O(N);
  • 空间复杂度 O(N):哈希表和栈使用线性的空间大小(哈希表实际只存 4 对映射,可视为常数级;栈在最坏情况下需容纳全部左括号,为 O(N))。

多语言代码实现

Python 实现

与文档一致的 Python 解法,完整实现见 lc_20_valid_parentheses.py:

class Solution: def isValid(self, s: str) -> bool: dic = {'{': '}', '[': ']', '(': ')', '?': '?'} stack = ['?'] for c in s: if c in dic: stack.append(c) elif dic[stack.pop()] != c: return False return len(stack) == 1

Java 实现

与文档一致的 Java 解法,完整实现见 lc_20_valid_parentheses.java:

class Solution { private static final Map<Character,Character> map = new HashMap<Character,Character>(){{ put('{','}'); put('[',']'); put('(',')'); put('?','?'); }}; public boolean isValid(String s) { if(s.length() > 0 && !map.containsKey(s.charAt(0))) return false; LinkedList<Character> stack = new LinkedList<Character>() {{ add('?'); }}; for(Character c : s.toCharArray()){ if(map.containsKey(c)) stack.addLast(c); else if(map.get(stack.removeLast()) != c) return false; } return stack.size() == 1; } }

Java 版本额外做了一层优化:若s非空且首字符不是左括号(即首字符就是右括号),直接返回false,这与哨兵'?'的作用互为印证,属于同一思路下的防御性写法。

C++ 参考实现

从仓库的 include.hpp 可以看到,C++ 代码目录统一引入了<stack><unordered_map>等标准库头文件,完全支持同思路的栈解法。仓库目前仅收录了该题的 Python 与 Java 两种实现,此处给出可对照阅读的 C++ 参考写法:

#include <stack> #include <string> #include <unordered_map> using namespace std; class Solution { public: bool isValid(string s) { unordered_map<char, char> dic = {{'{', '}'}, {'[', ']'}, {'(', ')'}, {'?', '?'}}; stack<char> stk; stk.push('?'); for (char c : s) { if (dic.count(c)) stk.push(c); else if (dic[stk.top()] != c) return false; else stk.pop(); } return stk.size() == 1; } };

仓库源码印证与运行方式

仓库中的 Python 与 Java 实现文件在题解代码基础上补齐了驱动代码(Driver Code)Solution实例化后调用isValid,并将结果打印输出,便于本地直接运行验证。同时,Python 实现顶部通过from include import *引入了 include 公共模块,Java 实现则通过import include.*引入 include 公共工具类,保持了与仓库其他 88 道精选题一致的工程组织方式。

运行方式(以 Python 为例):

# 在工作区根目录执行 python selected_coding_interview/codes/python/lc_20_valid_parentheses.py

替换文件末尾test_input变量即可测试"()""()[]{}""(]""([)]""{[]}"等典型用例。

举一反三:栈的更多应用

「有效的括号」是栈这一数据结构的经典敲门砖,理解了"栈顶即最近未闭合元素"这一直觉后,可以继续挑战仓库中的同族题目:

  • LCR 148. 验证图书取出顺序(即 LeetCode 946)——同样是"入栈/出栈序列"的判定问题,解法结构与本题的出栈模拟高度相似,对应代码见 lc_946_validate_stack_sequences.py;
    1. 最小栈(LCR 147)——在栈的基础上增加 O(1) 取最小值的辅助栈设计,进一步体会"用栈结构维护附加信息"的思路。

小结

本题的标准解法总结为四步:遇左括号入栈 → 遇右括号弹出栈顶比对 → 不匹配提前返回 false → 遍历结束检查是否只剩哨兵。借助哈希表把括号配对查询降到 O(1),借助哨兵'?'优雅规避空栈边界,最终实现 O(N) 时间、O(N) 空间的简洁解。这份题解连同 Python、Java 可运行源码均已收录在 LeetCode-Book 仓库的 selected_coding_interview 目录下,可作为刷题与面试复习的对照资料。

【免费下载链接】LeetCode-Book《剑指 Offer》《图解算法数据结构》《Krahets 笔面试精选 88 题》Python, Java, C++ 解题代码项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Book

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询