LeetCode 1019中等链表/单调栈
链表中的下一个更大节点 图解题解
这道题到底在问什么
给定链表 head,对每个节点返回其右侧第一个值更大的节点值;若不存在则为 0。
- 输入
- head = [2,1,5]
- 输出
- [5,5,0]
- 输入
- head = [2,7,4,3,5]
- 输出
- [7,0,5,5,0]
- 输入
- head = [1]
- 输出
- [0]
最优解:一步一步想明白
- 3记住:栈里存「还在等更大值」的下标,新值能压倒谁就给谁结算、弹出谁。下面逐帧套它。
- 4遍历链表:读到第 1 个节点(值 1),把它抄进数组 vals 的下标 0。
- 5遍历链表:读到第 2 个节点(值 7),把它抄进数组 vals 的下标 1。
- 6遍历链表:读到第 3 个节点(值 5),把它抄进数组 vals 的下标 2。
- 7遍历链表:读到第 4 个节点(值 1),把它抄进数组 vals 的下标 3。
- 8遍历链表:读到第 5 个节点(值 9),把它抄进数组 vals 的下标 4。
- 9遍历链表:读到第 6 个节点(值 2),把它抄进数组 vals 的下标 5。
- 10遍历链表:读到第 7 个节点(值 5),把它抄进数组 vals 的下标 6。
- 11遍历链表:读到第 8 个节点(值 1),把它抄进数组 vals 的下标 7。链表读完,vals 已就绪,下面切到数组上跑单调栈。
- 12vals 抄好了:柱子高度 = 节点值,柱子下方数字 = 下标。现在从左到右扫一遍,空栈起步,栈里只放「还在等更大值」的下标。
- 13轮到下标 0(高 1,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 14栈已空,没有可结算的。把当前下标 0 压栈,让它去等自己的下一个更大值。此刻 ans = [0, 0, 0, 0, 0, 0, 0, 0]。
- 15轮到下标 1(高 7,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 16栈顶是下标 0(高 1,标红),它严格小于当前的 7,说明 7 就是它右边第一个更大值。填 ans[0] = 7,把下标 0 弹出。
- 17栈已空,没有可结算的。把当前下标 1 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 0, 0, 0, 0, 0, 0, 0]。
- 18轮到下标 2(高 5,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 19栈顶下标 1(高 7)不比 5 矮,停止弹栈。把当前下标 2 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 0, 0, 0, 0, 0, 0, 0]。
- 20轮到下标 3(高 1,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 21栈顶下标 2(高 5)不比 1 矮,停止弹栈。把当前下标 3 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 0, 0, 0, 0, 0, 0, 0]。
- 22轮到下标 4(高 9,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 23栈顶是下标 3(高 1,标红),它严格小于当前的 9,说明 9 就是它右边第一个更大值。填 ans[3] = 9,把下标 3 弹出。
- 24栈顶是下标 2(高 5,标红),它严格小于当前的 9,说明 9 就是它右边第一个更大值。填 ans[2] = 9,把下标 2 弹出。
- 25栈顶是下标 1(高 7,标红),它严格小于当前的 9,说明 9 就是它右边第一个更大值。填 ans[1] = 9,把下标 1 弹出。
- 26栈已空,没有可结算的。把当前下标 4 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 9, 9, 9, 0, 0, 0, 0]。
- 27轮到下标 5(高 2,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 28栈顶下标 4(高 9)不比 2 矮,停止弹栈。把当前下标 5 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 9, 9, 9, 0, 0, 0, 0]。
- 29轮到下标 6(高 5,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 30栈顶是下标 5(高 2,标红),它严格小于当前的 5,说明 5 就是它右边第一个更大值。填 ans[5] = 5,把下标 5 弹出。
- 31栈顶下标 4(高 9)不比 5 矮,停止弹栈。把当前下标 6 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 9, 9, 9, 0, 5, 0, 0]。
- 32轮到下标 7(高 1,紫色)。先反复比对栈顶:看栈里有没有正在等它的矮个子。
- 33栈顶下标 6(高 5)不比 1 矮,停止弹栈。把当前下标 7 压栈,让它去等自己的下一个更大值。此刻 ans = [7, 9, 9, 9, 0, 5, 0, 0]。
- 34扫完整条数组。栈里还剩下标 4, 6, 7(蓝色),它们右边再没有更大的值,答案保持 0。最终 ans = [7, 9, 9, 9, 0, 5, 0, 0],和开头一致。
⚠️ 容易写错的地方
✗ 错:栈里存「值」而不是「下标」
✓ 对:存下标
结算时要靠下标把更大值填回 ans 的对应位置;只存值就不知道该填到哪一格
✗ 错:弹栈用「小于等于」
✓ 对:严格小于才弹(vals[栈顶] < vals[i])
相等不算「更大」,遇到相等不能弹、不能结算,否则答案错
✗ 错:对每个节点向右暴力找更大值
✓ 对:单调栈一遍扫
暴力是 O(n²);单调栈让每个下标进出栈各一次,降到 O(n)
完整代码(Python / C++ / Java)
Python
from typing import Optional, List
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def nextLargerNodes(self, head: Optional[ListNode]) -> List[int]:
vals = []
while head:
vals.append(head.val)
head = head.next
ans = [0] * len(vals)
st = []
for i, v in enumerate(vals):
while st and vals[st[-1]] < v:
ans[st.pop()] = v
st.append(i)
return ansC++
#include <algorithm>
#include <stack>
#include <vector>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
class Solution {
public:
vector<int> nextLargerNodes(ListNode* head) {
vector<int> vals;
while (head) { vals.push_back(head->val); head = head->next; }
vector<int> ans(vals.size()), st;
for (int i = 0; i < (int)vals.size(); ++i) {
while (!st.empty() && vals[st.back()] < vals[i]) { ans[st.back()] = vals[i]; st.pop_back(); }
st.push_back(i);
}
return ans;
}
};Java
import java.util.*;
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
class Solution {
public int[] nextLargerNodes(ListNode head) {
ArrayList<Integer> vals = new ArrayList<>();
while (head != null) { vals.add(head.val); head = head.next; }
int[] ans = new int[vals.size()];
Deque<Integer> st = new ArrayDeque<>();
for (int i = 0; i < vals.size(); i++) {
while (!st.isEmpty() && vals.get(st.peek()) < vals.get(i)) ans[st.pop()] = vals.get(i);
st.push(i);
}
return ans;
}
}复杂度
时间
O(n)
遍历链表 O(n);扫描时每个下标最多进栈一次、出栈一次,均摊下来仍是 O(n)
空间
O(n)
数组 vals 占 O(n);单调栈最坏(严格递减时)装下全部 n 个下标
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 链表中的下一个更大节点 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
单调栈为什么能把 O(n²) 降到 O(n)?+
关键是每个下标一生只进栈一次、出栈一次。新值一来,就把栈里所有比它矮的一次性结算并弹掉,这些被弹掉的之后再也不会被访问。总的进出栈次数是 O(n),所以整体线性。
这题和「每日温度」(lc739)是一回事吗?+
本质完全一样,都是「下一个更大元素」的单调栈模板。区别只在:这里输入是链表,要先遍历转成数组;以及没有更大值时填 0,而不是像温度题填 0 天。模板和弹栈逻辑一模一样。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 链表中的下一个更大节点 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。