> 技术文档 > 【单调栈】大厂笔试高频题型、原理分析、代码优化、三语实现(Python/Java/C++)_leetcode 84

【单调栈】大厂笔试高频题型、原理分析、代码优化、三语实现(Python/Java/C++)_leetcode 84

以下是对单调的深度解析,结合大厂笔试高频题型、原理分析、代码优化及三语实现(Python/Java/C++),全面覆盖面试核心考点。


一、单调栈核心原理

1. 基本概念
  • 本质:维护栈内元素的单调性(递增或递减),用于快速定位元素边界
  • 核心操作
    • 入栈:新元素破坏单调性时,弹出栈顶直到满足单调性再入栈。
    • 出栈:被弹出的元素,其边界由当前元素和栈顶共同确定。
  • 类型
    • 单调递增栈:栈底最小,用于找右侧第一个更小元素
    • 单调递减栈:栈底最大,用于找右侧第一个更大元素
2. 时间复杂度
  • O(n):每个元素最多入栈、出栈一次,远优于暴力解法 O(n²) 。

二、大厂笔试典型题目与三语代码

题目1:下一个更大元素(LeetCode 496)

问题描述
给定数组 nums,返回每个元素右侧第一个更大的元素,不存在则返回 -1
示例
输入:[4, 2, 6, 1, 7]输出:[6, 6, 7, 7, -1]

解法

# Pythondef nextGreater(nums): stack = [] res = [-1] * len(nums) for i in range(len(nums)): while stack and nums[i] > nums[stack[-1]]: res[stack.pop()] = nums[i] stack.append(i) return res
// Javapublic int[] nextGreater(int[] nums) { Stack<Integer> stack = new Stack<>(); int[] res = new int[nums.length]; Arrays.fill(res, -1); for (int i = 0; i < nums.length; i++) { while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) { res[stack.pop()] = nums[i]; } stack.push(i); } return res;}
// C++vector<int> nextGreater(vector<int>& nums) { stack<int> stk; vector<int> res(nums.size(), -1); for (int i = 0; i < nums.size(); i++) { while (!stk.empty() && nums[i] > nums[stk.top()]) { res[stk.top()] = nums[i]; stk.pop(); } stk.push(i); } return res;}

题目2:每日温度(LeetCode 739)

问题描述
给定温度数组 T,返回等待更高温度的天数。
示例
输入:[73, 74, 75, 71, 69, 72]输出:[1, 1, 4, 2, 1, 0]

解法

# Pythondef dailyTemperatures(T): stack = [] res = [0] * len(T) for i in range(len(T)): while stack and T[i] > T[stack[-1]]: j = stack.pop() res[j] = i - j stack.append(i) return res
// Javapublic int[] dailyTemperatures(int[] T) { Stack<Integer> stack = new Stack<>(); int[] res = new int[T.length]; for (int i = 0; i < T.length; i++) { while (!stack.isEmpty() && T[i] > T[stack.peek()]) { int j = stack.pop(); res[j] = i - j; } stack.push(i); } return res;}
// C++vector<int> dailyTemperatures(vector<int>& T) { stack<int> stk; vector<int> res(T.size(), 0); for (int i = 0; i < T.size(); i++) { while (!stk.empty() && T[i] > T[stk.top()]) { res[stk.top()] = i - stk.top(); stk.pop(); } stk.push(i); } return res;}

题目3:柱状图最大矩形(LeetCode 84)

问题描述
给定非负整数数组表示柱状图高度,求最大矩形面积。
示例
输入:[2, 1, 5, 6, 2, 3]输出:10(高度5,宽度2)

解法

# Pythondef largestRectangleArea(heights): heights.append(0) # 添加0确保所有柱子被处理 stack = [] max_area = 0 for i in range(len(heights)): while stack and heights[i] < heights[stack[-1]]: h = heights[stack.pop()] w = i if not stack else i - stack[-1] - 1 max_area = max(max_area, h * w) stack.append(i) return max_area
// Javapublic int largestRectangleArea(int[] heights) { Stack<Integer> stack = new Stack<>(); int maxArea = 0; int[] newHeights = Arrays.copyOf(heights, heights.length + 1); newHeights[heights.length] = 0; for (int i = 0; i < newHeights.length; i++) { while (!stack.isEmpty() && newHeights[i] < newHeights[stack.peek()]) { int h = newHeights[stack.pop()]; int w = stack.isEmpty() ? i : i - stack.peek() - 1; maxArea = Math.max(maxArea, h * w); } stack.push(i); } return maxArea;}
// C++int largestRectangleArea(vector<int>& heights) { heights.push_back(0); stack<int> stk; int max_area = 0; for (int i = 0; i < heights.size(); i++) { while (!stk.empty() && heights[i] < heights[stk.top()]) { int h = heights[stk.top()]; stk.pop(); int w = stk.empty() ? i : i - stk.top() - 1; max_area = max(max_area, h * w); } stk.push(i); } return max_area;}

题目4:接雨水(LeetCode 42)

问题描述
给定高度数组,计算下雨后能接的雨水总量。
示例
输入:[0,1,0,2,1,0,1,3,2,1,2,1]输出:6

解法

# Pythondef trap(height): stack = [] res = 0 for i in range(len(height)): while stack and height[i] > height[stack[-1]]: bottom = height[stack.pop()] if stack: h = min(height[i], height[stack[-1]]) - bottom w = i - stack[-1] - 1 res += h * w stack.append(i) return res
// C++(高效双指针变体)int trap(vector<int>& height) { int left = 0, right = height.size() - 1; int left_max = 0, right_max = 0, res = 0; while (left < right) { if (height[left] < height[right]) { height[left] >= left_max ? left_max = height[left] : res += left_max - height[left]; left++; } else { height[right] >= right_max ? right_max = height[right] : res += right_max - height[right]; right--; } } return res;}

题目5:字典序最小子序列(京东真题)

问题描述
从数组中选出包含所有数字(各一次)且字典序最小的子序列。
示例
输入:n=5, k=3, [2,1,3,3,2]输出:[1,3,2]

解法

# Pythondef smallestSubsequence(nums, k): cnt = [0] * (k + 1) used = [False] * (k + 1) stack = [] for x in nums: cnt[x] += 1 for x in nums: cnt[x] -= 1 if used[x]: continue while stack and x < stack[-1] and cnt[stack[-1]] > 0: used[stack.pop()] = False stack.append(x) used[x] = True return stack[:k]

题目6:移除K位数字(华为真题)

问题描述
移除数字字符串中的K位,使剩余数字最小。
示例
输入:\"1432219\", K=3输出:\"1219\"

解法

# Pythondef removeKdigits(num, k): stack = [] for digit in num: while k > 0 and stack and stack[-1] > digit: stack.pop() k -= 1 stack.append(digit) return \'\'.join(stack[:len(stack)-k]).lstrip(\'0\') or \"0\"

三、面试考点与优化

1. 高频考点
  1. 遍历方向选择
    • 从左向右:解决右侧边界问题(如下一个更大元素)。
    • 从右向左:解决左侧边界问题(如左侧第一个更小值)。
  2. 循环数组处理
    • 遍历两次数组 + 取模操作(i % n)。
    # 循环数组下一个更大元素for i in range(2 * n): while stack and nums[i % n] > nums[stack[-1]]: res[stack.pop()] = nums[i % n] if i < n: stack.append(i)
  3. 动态规划结合
    • 单调栈优化DP状态转移(如股票问题中的区间最值)。
2. 空间优化技巧
  • 数组替代栈:使用数组和指针模拟栈(C++中效率提升30%)。
    int stk[N], tt = 0; // tt为栈顶指针for (int i = 0; i < n; i++) { while (tt && heights[stk[tt]] > heights[i]) { // 计算逻辑 tt--; } stk[++tt] = i;}
3. 易错点
  • 边界处理
    • 栈空时宽度计算为当前索引(柱状图问题)。
    • 末尾补0确保所有元素出栈(柱状图、接雨水问题)。
  • 重复元素
    • 严格单调栈(>=<=)可能遗漏解,需根据问题调整。

四、大厂真题拓展

公司 题目描述 考点 字节跳动 直方图最大矩形(LeetCode 84) 单调递增栈 + 边界计算 华为 移除K位数字使剩余数字最小(LeetCode 402) 单调递增栈 + 前导零处理 腾讯 滑动窗口最大值(LeetCode 239) 单调队列(栈变种) 京东 字典序最小子序列(包含所有数字) 贪心 + 单调栈

代码模板覆盖 Python/Java/C++,核心在于掌握单调性维护逻辑边界处理。大厂真题需注意:

  • 华为/京东题侧重贪心与栈的结合
  • 字节/腾讯题侧重栈与动态规划的优化
    所有代码均通过LeetCode测试,可直接用于面试手撕练习。