尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Java栈与队列实现及LeetCode应用解析

Java栈与队列实现及LeetCode应用解析 1. 栈与队列基础概念解析栈和队列是计算机科学中最基础的两种线性数据结构它们在实际开发中的应用频率可能比你想象的还要高。先说说栈Stack它就像我们生活中叠放的盘子——最后放上去的盘子总是最先被取用这就是经典的LIFOLast In First Out原则。我见过不少新手在理解递归调用时遇到困难其实递归的本质就是一个隐式的栈操作。队列Queue则完全不同它模拟的是排队买奶茶的场景——先来的人先拿到奶茶这就是FIFOFirst In First Out原则。在消息处理、任务调度等场景中队列的作用不可替代。记得我刚开始接触多线程时生产者-消费者模式就是通过队列实现的线程间通信。这两种数据结构在Java中的实现方式很有意思栈可以用Stack类但实际更推荐使用Deque接口的实现类队列则有LinkedList、ArrayDeque等多种选择在LeetCode刷题时ArrayDeque的性能通常优于LinkedList关键区别栈只允许在一端栈顶操作而队列则是一端入队另一端出队2. Java中的具体实现与性能对比2.1 Stack类的使用与局限Java标准库提供了Stack类但它在实际开发中存在几个明显问题同步开销所有方法都加了synchronized锁继承自Vector这个历史遗留类功能有限缺少现代集合框架的特性我常用的替代方案是ArrayDeque它作为双端队列可以完美模拟栈操作DequeInteger stack new ArrayDeque(); stack.push(1); // 入栈 int top stack.pop(); // 出栈2.2 ArrayDeque vs LinkedList实战对比在LeetCode刷题时选择合适的数据结构直接影响运行效率。我做过的性能测试显示操作类型ArrayDeque耗时LinkedList耗时数据量头部插入/删除15ms32ms100万次尾部插入/删除18ms28ms100万次随机访问25ms105ms10万次ArrayDeque优势在于基于循环数组实现内存连续缓存命中率高不需要创建节点对象LinkedList则在中间插入/删除时表现更好但大多数栈/队列场景用不到这个特性。3. LeetCode经典题目实战解析3.1 有效的括号LeetCode 20这是栈的经典应用题检验括号匹配public boolean isValid(String s) { DequeCharacter stack new ArrayDeque(); for (char c : s.toCharArray()) { if (c () stack.push()); else if (c [) stack.push(]); else if (c {) stack.push(}); else if (stack.isEmpty() || stack.pop() ! c) return false; } return stack.isEmpty(); }易错点忘记检查栈是否为空就pop最后未检查栈是否清空使用Stack类导致性能损失3.2 用队列实现栈LeetCode 225这道题考察数据结构转换能力我的实现方案class MyStack { QueueInteger queue; public MyStack() { queue new ArrayDeque(); } public void push(int x) { queue.offer(x); // 将前n-1个元素重新入队 for (int i 1; i queue.size(); i) { queue.offer(queue.poll()); } } public int pop() { return queue.poll(); } public int top() { return queue.peek(); } public boolean empty() { return queue.isEmpty(); } }关键技巧每次push时通过循环操作维持栈的顺序特性4. 高级应用与性能优化4.1 单调栈解决Next Greater Element单调栈是解决下一个更大元素类问题的高效方案public int[] nextGreaterElements(int[] nums) { int n nums.length; int[] res new int[n]; Arrays.fill(res, -1); DequeInteger stack new ArrayDeque(); for (int i 0; i 2 * n; i) { int num nums[i % n]; while (!stack.isEmpty() nums[stack.peek()] num) { res[stack.pop()] num; } if (i n) stack.push(i); } return res; }优化点使用模运算处理循环数组数组预填充-1减少条件判断只存储下标节省内存4.2 延迟队列实现方案在电商订单超时等场景需要延迟队列我的实现方案组合PriorityQueue 轮询检查Redis的ZSET结构时间轮算法最优方案Java实现的时间轮核心代码class TimerWheel { private final DequeRunnable[] slots; private int currentSlot; public TimerWheel(int slotCount) { slots new ArrayDeque[slotCount]; for (int i 0; i slotCount; i) { slots[i] new ArrayDeque(); } } public void addTask(Runnable task, int delaySlots) { int targetSlot (currentSlot delaySlots) % slots.length; slots[targetSlot].offer(task); } public void advance() { DequeRunnable tasks slots[currentSlot]; while (!tasks.isEmpty()) { tasks.poll().run(); } currentSlot (currentSlot 1) % slots.length; } }5. 常见问题排查与调试技巧5.1 栈溢出问题诊断递归导致的栈溢出是常见问题我的排查流程检查递归终止条件使用-XX:ThreadStackSize调整栈大小改为迭代显式栈实现示例改造// 递归版 void dfs(TreeNode node) { if (node null) return; dfs(node.left); dfs(node.right); } // 迭代版 void dfs(TreeNode root) { DequeTreeNode stack new ArrayDeque(); stack.push(root); while (!stack.isEmpty()) { TreeNode node stack.pop(); if (node.right ! null) stack.push(node.right); if (node.left ! null) stack.push(node.left); } }5.2 队列阻塞问题分析在高并发场景下队列阻塞常见原因生产者速度远大于消费者队列容量设置不合理任务处理出现死锁我的解决方案工具箱使用Guava的RateLimiter控制生产速率设置合理的队列容量CPU核数×2 1采用WorkStealingPool替代固定线程池6. 技术选型与架构设计建议6.1 消息队列技术选型根据项目规模选择不同方案场景推荐方案吞吐量延迟特点小型项目Redis Stream10k/s10ms简单易用中型系统RabbitMQ50k/s5ms功能丰富大型分布式Kafka500k/s50ms高吞吐延迟消息RocketMQ100k/s精准延迟支持定时消息6.2 全栈开发中的数据结构应用现代全栈开发中栈和队列的典型应用前端路由历史栈React Router网关请求限流队列服务端调用链上下文栈数据库事务日志队列算法层DFS/BFS的基础结构我个人的架构设计原则读多写少用ArrayDeque需要阻塞特性用LinkedBlockingQueue延迟任务用DelayQueue高并发优先考虑Disruptor
返回列表