原题链接: https://leetcode.com/problems/next-greater-node-in-linked-list/
1. 题目介绍
We are given a linked list with head as the first node. Let’s number the nodes in the list: node_1, node_2, node_3, … etc.
Each node may have a next larger value: for node_i, next_larger(node_i) is the node_j.val such that j > i, node_j.val > node_i.val, and j is the smallest possible choice. If such a j does not exist, the next larger value is 0.
Return an array of integers answer, where answer[i] = next_larger(node_{i+1}).
给出一个链表,按照节点出现的顺序,我们给链表节点编号:
node_1, node_2, node_3, …
每一个节点都有 “下一个最大值” 。对于 node_i 来说,它的下一个最大值是node_j.val。
这个 j 需要满足下面三个条件:
- j > i
- node_j.val > node_i.val
- 如果满足上面两个条件的 j 不止一个,那么就从中取最小的 j
如果不存在同时满足上面三个条件的 j ,则 node_i 的 “下一个最大值” 是0.
返回一个整数数组 answer,answer[ i ] = next_larger(node_{i+1}). 也就是第 i 个数组元素的值,是第 i +1 个节点的“下一个最大值”。
Note that in the example inputs (not outputs) below, arrays such as [2,1,5] represent the serialization of a linked list with a head node value of 2, second node value of 1, and third node value of 5.
需要注意,下面举例中,输入 [2,1,5] 代表了链表 2 -> 1 -> 5.
Example 1:
Input: [2,1,5]
Output: [5,5,0]
Example 2:
Input: [2,7,4,3,5]
Output: [7,0,5,5,0]
Example 3:
Input: [1,7,5,1,9,2,5,1]
Output: [7,9,9,9,0,5,0,0]
Note:
链表中每个节点的val值的范围 : 1 <= node.val <= 10^9
链表的长度范围是:[0, 10000].
2. 解题思路
这个题目的难点在于想办法降低时间复杂度。
如果用暴力搜索的方法,遍历整个链表,每经过一个节点,都要去搜索这个节点后面的所有节点来寻找符合要求的 j ,这个时间复杂度是O( n2 )
为了降低时间复杂度,我们主要有两个思路:
- 改变遍历的顺序,将遍历顺序改成从后向前。
- 将已经搜索到的满足条件的节点存储起来,不用每次都搜一遍。
将这两个思路结合在一起,就是本题的解法。
对于思路1,由于题目给出的是单项链表,只能从前向后遍历。所以我们要做的第一步就是将链表改成一个可以从后向前遍历的结构。数组、ArrayList都可以,我用的是ArrayList,因为链表的长度不确定。
对于思路2,我们需要使用一个数据结构将搜过的节点存起来。当出现多个满足条件的节点时,要取最前面的那个。在这种情况下我们可以利用堆栈。设置一个堆栈,存储节点 i 之后所有满足条件的 j 。每次只从堆栈的顶部取走那个最前面的 j 即可。如果堆栈为空,说明没有满足条件的节点,这个 i 的answer[ i ]就是0。
通过上述优化,时间复杂度可以降低为 O(n)
实现代码
class Solution {
public int[] nextLargerNodes(ListNode head) {
if(head == null ){
return new int[0];
}
//由于需要倒序计算,因此先将链表转换为ArrayList
List<Integer> list = new ArrayList<Integer>();
ListNode cur = head;
while(cur != null){
list.add(cur.val);
cur = cur.next;
}
int length = list.size();
int [] ans = new int[length];//ans就是要返回的数组
//堆栈中存放所有满足条件的节点val值
Stack<Integer> s = new Stack<Integer>();
for(int i = length-1 ; i >= 0; i-- ){
//如果堆栈不空,就要弹出所有小于当前节点的值
//以保证堆栈中存储的都是“所有满足条件的节点val值”
while( s.isEmpty() == false ){
int temp = s.peek();//peek函数取栈顶元素,但是不弹出
if(temp > list.get(i)){
ans[i] = temp;
s.push(list.get(i));
break;
}
else{//将不符合条件的剔除
//如果堆栈中没有满足条件的值,这个堆栈会被删空
s.pop();
}
}
//堆栈为空,可能是在上面的while循环中被删空了,可能是第一遍循环还没有放入值
//这时就应该将第 i 个节点中的val值放入堆栈,ans[i] 直接放入0
if(s.isEmpty() == true){
ans[i] = 0;
s.push(list.get(i));
}
}
return ans;
}
}