程序员代码面试指南第二版 8.单调栈结构(普通及进阶)
题目描述(不含重复值的数组)
给定一个不含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。
输入描述:
第一行输入一个数字 n,表示数组 arr 的长度。
以下一行输出 n个数字,表示数组的值。
输出描述:
输出n行,每行两个数字 L 和 R,如果不存在,则值为-1,下标从0开始。
示例1
输入
7
3 4 1 5 6 2 7
输出
-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1
第一次做; 数组中不含重复元素, 使用单调栈结构 ; 栈中存储的是索引;
单调栈的特点及处理流程
- 要保证栈底到栈顶严格递增
- 一共两个阶段: 完整遍历一遍数组; 接着进行清算阶段
- 遍历数组阶段:
- 如果当前元素大于栈顶索引对应的值, 则将当前元素的索引压栈
- 如果当前元素小于栈顶索引对应的值, 弹出栈顶索引, 此时能得到被弹出的索引左右两侧比它小并且离它最近的值的索引, 具体地,
- 当前元素的索引就是该索引右侧的最终结果
- 如果栈为空, 则该索引左侧没有元素了, 可以令左侧的结果为-1
- 如果栈不为空, 那新的栈顶索引就是该索引左侧的最终结果
- 清算阶段: 清算阶段的所有元素都没有右侧元素
- 如果栈不为空, 弹出栈顶索引, 该索引没有右侧结果, 可以令右侧结果为-1; 如果此时栈不为空, 那么该元素的左侧结果就是新栈顶索引; 如果此时栈为空, 该索引没有左侧结果, 可以令左侧结果为-1
import java.util.Scanner;
import java.util.Stack;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = Integer.valueOf(sc.nextLine());
String[] arr = sc.nextLine().split(" ");
String[] res = new String[n];
int index;
Stack<Integer> s = new Stack<>();
for(int i=0; i<n; i++){
if(s.isEmpty())
s.push(i);
else{
if(Integer.valueOf(arr[i]) > Integer.valueOf(arr[s.peek()])){
s.push(i);
}
else{
while(!s.isEmpty() && Integer.valueOf(arr[i]) < Integer.valueOf(arr[s.peek()])){
res[s.pop()] = s.isEmpty() ? "-1 "+i : s.peek()+" "+i;
}
s.push(i);
}
}
}
while(!s.isEmpty()){
res[s.pop()] = s.isEmpty()? "-1 -1" : s.peek()+" -1";
}
for(int i=0; i<n; i++){
System.out.println(res[i]);
}
}
}
题目描述(含有重复值的数组)
题目描述
给定一个可能含有重复值的数组 arr,找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置。返回所有位置相应的信息。
输入描述:
第一行输入一个数字 n,表示数组 arr 的长度。
以下一行输入 n 个数字,表示数组的值
输出描述:
输出n行,每行两个数字 L 和 R,如果不存在,则值为 -1,下标从 0 开始。
示例1
输入
7
3 4 1 5 6 2 7
输出
-1 2
0 2
-1 -1
2 5
3 5
2 -1
5 -1
第一次做; 含有重复元素的数组, 此时使用单调栈; 大体流程和普通版本的单调栈一样, 只不过多了相等元素的处理; 操作索引要小心, 一开始写错了几处
(考虑相等元素)单调栈的特点及处理流程
- 要保证栈底到栈顶非递减(不再是严格递增了)
- 一共两个阶段: 完整遍历一遍数组; 接着进行清算阶段
- 遍历数组阶段:
- 如果当前元素大于栈顶ArrayList中第一个元素对应的值, 则将当前元素的索引压栈
- 如果当前元素等于栈顶ArrayList中第一个元素对应的值, 则将当前元素的索引加入到栈顶的ArrayList中
- 如果当前元素小于栈顶ArrayList中第一个元素对应的值, 弹出栈顶ArrayList, 此时能得到该ArrayList左右两侧离它最近的值的索引, 具体地,
- 当前元素的索引就是该ArrayList右侧的最终结果
- 如果栈为空, 则该ArrayList左侧没有元素了, 可以令左侧的结果为-1
- 如果栈不为空, 那新的栈顶ArrayList中的最后一个索引就是该ArrayList左侧的最终结果
- 清算阶段:
- 弹出栈顶ArrayList, 该ArrayList没有右侧结果
- 如果栈不为空, 该ArrayList的左侧结果就是新栈顶ArrayList中的最后一个元素
- 如果栈为空, 该ArrayList没有左侧结果
import java.util.Scanner;
import java.util.Stack;
import java.util.ArrayList;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = Integer.valueOf(sc.nextLine());
String[] arr = sc.nextLine().split(" ");
Stack<ArrayList<Integer>> s = new Stack<>();
ArrayList<Integer> al;
String[] res = new String[n];
for(int i=0; i<n; i++){
if(s.isEmpty()){
s.push(new ArrayList<Integer>());
s.peek().add(i);
}
else{
int stackTop = Integer.valueOf(arr[s.peek().get(0)]);
int curr = Integer.valueOf(arr[i]);
if(stackTop < curr){
s.push(new ArrayList<Integer>());
s.peek().add(i);
}
else if(stackTop == curr){
s.peek().add(i);
}
else{
while(!s.isEmpty() && stackTop > curr){
al = s.pop();
for(int j=0; j<al.size(); j++){
res[al.get(j)] = s.isEmpty() ? "-1 " + i : s.peek().get(s.peek().size()-1) + " " + i;
}
if(s.isEmpty())
break;
else
stackTop = Integer.valueOf(arr[s.peek().get(s.peek().size()-1)]);
}
if(!s.isEmpty() && Integer.valueOf(arr[s.peek().get(0)]) == curr)
s.peek().add(i);
else{
s.push(new ArrayList<Integer>());
s.peek().add(i);
}
}
}
}
while(!s.isEmpty()){
al = s.pop();
for(int i=0; i<al.size(); i++){
res[al.get(i)] = s.isEmpty()? "-1 -1" : s.peek().get(s.peek().size()-1) + " -1";
}
}
for(int i=0; i<res.length; i++)
System.out.println(res[i]);
}
}