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

《常见高频算法题 Java 解法实战精讲(2):堆栈与递归》

🔍常见高频算法题 Java 解法实战精讲(2):堆栈与递归

🧠写在前面:堆栈与递归题型为何频繁出现在面试中?

堆栈(Stack)与递归(Recursion)类题目属于经典的算法考察方向,原因如下:

  • ✅ 数据结构基础扎实度测试:考查是否真正理解“先进后出”的栈结构。

  • ✅ 代码逻辑清晰与抽象能力:递归常被用于处理树、图等结构,能快速验证抽象思维。

  • ✅ Java 线程控制/执行顺序的理解:堆栈和线程调度关系紧密,是并发考点基础。

  • ✅ 模板化解题能力评估:是否掌握栈操作与 DFS/BFS 的通用套路,体现工程实践力。

文章目录

  • 🔍常见高频算法题 Java 解法实战精讲(2):堆栈与递归
    • 🧠写在前面:堆栈与递归题型为何频繁出现在面试中?
  • 一、堆栈与递归的核心地位
    • 💡 堆栈与递归的关系
    • ⚠️ 面试高频原因
  • 二、堆栈类经典题
    • 💡 2.1 有效的括号(Valid Parentheses)
    • 💡 2.2 逆波兰表达式求值
  • 三、DFS与BFS模板实战
    • 💡 3.1 二叉树的最大深度(DFS模板)
    • 💡 3.2 图的最短路径(BFS模板)
  • 四、多线程算法题
    • 💡 4.1 交替打印FooBar
    • 💡 4.2 按序打印ABC
  • 五、高频面试套路总结
    • 💡 堆栈类题目套路
    • 🔄 搜索算法模板
    • ⚡️ 多线程解题框架
    • 🛡️ 面试避坑指南

一、堆栈与递归的核心地位

💡 堆栈与递归的关系

递归
函数调用栈
栈帧
局部变量
返回地址
参数传递

⚠️ 面试高频原因

原因考察点出现频率
基础能力函数调用机制90%
思维转换递归转迭代75%
系统设计栈溢出防护60%
复杂问题树/图遍历85%

二、堆栈类经典题

💡 2.1 有效的括号(Valid Parentheses)

​​题目描述​​:

给定只包含’(', ‘)’, ‘{’, ‘}’, ‘[’, ']'的字符串,判断括号是否有效

​​Java解法​​:

public boolean isValid(String s) {Deque<Character> stack = new ArrayDeque<>();Map<Character, Character> map = Map.of(')', '(', '}', '{', ']', '[');for (char c : s.toCharArray()) {if (map.containsValue(c)) {stack.push(c);} else if (stack.isEmpty() || stack.pop() != map.get(c)) {return false;}}return stack.isEmpty();
}

复杂度分析​​:

  • 时间复杂度:O(n) 单次遍历
  • 空间复杂度:O(n) 栈空间
    ​​常见陷阱​​:
// 错误:未处理空栈情况
if (stack.pop() != map.get(c)) // 当栈空时抛出异常// 正确:先检查栈空
if (stack.isEmpty() || stack.pop() != map.get(c))

💡 2.2 逆波兰表达式求值

​​题目描述​​:

根据逆波兰表示法(后缀表达式)求值,如[“2”,“1”,“+”,“3”,“*”]→ (2+1)*3=9

​​Java解法​​:

public int evalRPN(String[] tokens) {Deque<Integer> stack = new ArrayDeque<>();for (String token : tokens) {switch (token) {case "+":stack.push(stack.pop() + stack.pop());break;case "-":int b = stack.pop(), a = stack.pop();stack.push(a - b);break;case "*":stack.push(stack.pop() * stack.pop());break;case "/":int divisor = stack.pop(), dividend = stack.pop();stack.push(dividend / divisor);break;default:stack.push(Integer.parseInt(token));}}return stack.pop();
}

​​操作流程​​:

2
入栈
1
入栈
+
弹出1和2 计算2+1=3入栈
3
入栈
*
弹出3和3 计算3 * 3=9入栈

复杂度分析​​:

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

三、DFS与BFS模板实战

💡 3.1 二叉树的最大深度(DFS模板)

​​题目描述​​:

求二叉树的最大深度

​​递归解法​​:

public int maxDepth(TreeNode root) {if (root == null) return 0;int left = maxDepth(root.left);int right = maxDepth(root.right);return Math.max(left, right) + 1;
}

​​迭代解法​​:

public int maxDepth(TreeNode root) {if (root == null) return 0;Deque<TreeNode> stack = new ArrayDeque<>();Deque<Integer> depths = new ArrayDeque<>();stack.push(root);depths.push(1);int max = 0;while (!stack.isEmpty()) {TreeNode node = stack.pop();int depth = depths.pop();max = Math.max(max, depth);if (node.right != null) {stack.push(node.right);depths.push(depth + 1);}if (node.left != null) {stack.push(node.left);depths.push(depth + 1);}}return max;
}

​​DFS模板总结​​:

// 递归模板
void dfs(Node node) {if (终止条件) return;for (Node child : node.children) {dfs(child);}
}// 迭代模板
void dfs(Node root) {Stack<Node> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {Node node = stack.pop();for (Node child : node.children) {stack.push(child);}}
}

💡 3.2 图的最短路径(BFS模板)

​​题目描述​​:

在无权图中求从起点到终点的最短路径

​​Java解法​​:

public int shortestPath(int[][] graph, int start, int end) {Queue<Integer> queue = new LinkedList<>();boolean[] visited = new boolean[graph.length];int[] distance = new int[graph.length];queue.offer(start);visited[start] = true;while (!queue.isEmpty()) {int node = queue.poll();if (node == end) return distance[node];for (int neighbor : graph[node]) {if (!visited[neighbor]) {visited[neighbor] = true;distance[neighbor] = distance[node] + 1;queue.offer(neighbor);}}}return -1; // 不可达
}

​​BFS模板总结​​:

void bfs(Node start) {Queue<Node> queue = new LinkedList<>();Set<Node> visited = new HashSet<>();queue.offer(start);visited.add(start);while (!queue.isEmpty()) {Node node = queue.poll();for (Node neighbor : node.neighbors) {if (!visited.contains(neighbor)) {visited.add(neighbor);queue.offer(neighbor);}}}
}

​​复杂度对比​​:

算法时间复杂度空间复杂度适用场景
DFSO(V+E)O(h)深度优先/路径存在
BFSO(V+E)O(w)最短路径/层级遍历

四、多线程算法题

💡 4.1 交替打印FooBar

​​题目描述​​:

两个线程交替打印"foo"和"bar"各n次

​​Semaphore解法​​:

class FooBar {private int n;private Semaphore fooSem = new Semaphore(1);private Semaphore barSem = new Semaphore(0);public FooBar(int n) { this.n = n; }public void foo(Runnable printFoo) throws InterruptedException {for (int i = 0; i < n; i++) {fooSem.acquire();printFoo.run();barSem.release();}}public void bar(Runnable printBar) throws InterruptedException {for (int i = 0; i < n; i++) {barSem.acquire();printBar.run();fooSem.release();}}
}

💡 4.2 按序打印ABC

​​题目描述​​:

三个线程按顺序循环打印"A"“B”“C”

​​ReentrantLock解法​​:

class ABCPrinter {private ReentrantLock lock = new ReentrantLock();private Condition conditionA = lock.newCondition();private Condition conditionB = lock.newCondition();private Condition conditionC = lock.newCondition();private int state = 0; // 0:A, 1:B, 2:Cpublic void printA() throws InterruptedException {lock.lock();try {while (state != 0) conditionA.await();System.out.print("A");state = 1;conditionB.signal();} finally {lock.unlock();}}public void printB() throws InterruptedException {lock.lock();try {while (state != 1) conditionB.await();System.out.print("B");state = 2;conditionC.signal();} finally {lock.unlock();}}public void printC() throws InterruptedException {lock.lock();try {while (state != 2) conditionC.await();System.out.print("C");state = 0;conditionA.signal();} finally {lock.unlock();}}
}

​​多线程解题技巧​​:

多线程同步
信号量
锁+条件变量
volatile+自旋
阻塞队列
简单同步
精细控制
低延迟
解耦生产消费

五、高频面试套路总结

💡 堆栈类题目套路

  1. 括号匹配

    • 使用栈存储左括号
    • 遇到右括号时弹出匹配
    • 最后检查栈是否为空
  2. 表达式求值

    • 中缀转后缀(调度场算法)
    • 后缀表达式求值(操作数栈)
    • 处理运算符优先级
  3. 单调栈

    • 解决"下一个更大元素"问题
    • 保持栈内元素单调性
    • 时间复杂度O(n)

🔄 搜索算法模板

// DFS递归模板
void dfs(Node node, State state) {if (终止条件) {更新结果;return;}for (选择 : 当前选择列表) {做选择;dfs(下一节点, 新状态);撤销选择;}
}// BFS模板
void bfs(Node start) {Queue<Node> queue = new LinkedList<>();Set<Node> visited = new HashSet<>();queue.offer(start);visited.add(start);while (!queue.isEmpty()) {Node node = queue.poll();for (Node neighbor : node.neighbors) {if (!visited.contains(neighbor)) {visited.add(neighbor);queue.offer(neighbor);}}}
}

⚡️ 多线程解题框架

| 问题类型 | 推荐方案 | 关键点 |
|---------|---------|-------|
| **顺序控制** | 信号量 | acquire/release顺序 |
| **交替执行** | 条件变量 | await/signal精准通知 |
| **并行计算** | Future/CompletableFuture | 异步结果聚合 |
| **资源池** | Semaphore | 控制并发数 |

🛡️ 面试避坑指南

  1. 递归陷阱

    • 栈溢出:限制递归深度
    • 重复计算:使用记忆化
  2. DFS/BFS选择

    • DFS:路径存在性/所有解
    • BFS:最短路径/最少步数
  3. 多线程安全

    • 避免死锁:固定锁顺序
    • 防止饥饿:公平锁/超时
    • 可见性:volatile/原子类

​​模板是基础​​:掌握DFS/BFS标准写法
​​递归转迭代​​:避免栈溢出风险
​​线程安全第一​​:多线程优先考虑正确性
记住:​​算法面试不是考记忆,而是考思维

http://www.dtcms.com/a/319348.html

相关文章:

  • 【RabbitMQ面试精讲 Day 15】RabbitMQ故障转移与数据恢复
  • Java快速入门:包(Package)与导包(import)详解
  • PyTorch LSTM文本生成
  • VC6800智能相机:赋能智能制造,开启AI视觉新纪元
  • 一个设备或系统能够同时管理和监控两个摄像头的配
  • 基于Python+Vue+Mysql实现(物联网)智能大棚
  • Linux文件操作与用户管理
  • 【数据结构——并查集】
  • 第一个vue应用
  • python每日一题 贪心算法练习
  • OLMo 2 架构深度解析:开放语言模型的技术革命
  • QML与C++交互的方式
  • 【JavaEE】多线程之Thread类(上)
  • 健永科技工位RFID读卡器实现生产流水线物料跟踪与柔性化升级
  • 深入解析Go设计模式:责任链模式实战
  • Coze Studio 概览(八)--MCP服务管理
  • 【LeetCode】set和map相关算法题 前K个高频单词、随机链表的复制、两个数组的交集、环形链表
  • LeetCode算法日记 - Day 4: 三数之和、四数之和
  • LeetCode每日一题,2025-8-7
  • .NET PDF处理组件IronPDF:如何通过 AI 简化开发人员处理 PDF的方式
  • 第五节 Pyside6可视化界面
  • PCIe Base Specification解析(八)
  • 如何高效利用服装工厂跟单软件提升效率?
  • 【论坛系统自动化功能测试报告】
  • ⚖️ AI的“成本陷阱”:当技术狂欢遇上商业现实
  • XXL-JOB定时任务
  • 如何将服务器中的Docker镜像批量导出?
  • 论文Review BALM2 | 港大MARS出品!机器人顶刊TRO | 经典激光BA续作!BALM升级版 | TODO待精读
  • 远程制作《最后生还者》中的Xsens动作捕捉技术
  • 使用SETNX实现分布式锁