单调栈结构(进阶)

题目描述

给定一个可能含有重复值的数组 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

分析

我曾在该篇文章单调栈结构介绍过单调栈,但是这种单调栈只适合求不含有重复值的数组 arr 的每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置,而对于可能含有重复值的数组也就无可奈何。

我们可以改进单调栈的结构,我们仅将单调栈中的Integer元素改为链表的形式,链表即用来存取相等数组元素的下标,而单调栈仍然保持从栈顶到栈底元素idx所对应的arr[idx]由大到小(求最近最小值时)。

下面我们就一个简单的例子来演示单调栈的使用,比如现在有一数组arr[2,2,4,4,3]。

当遍历到数组的0号位时,由于栈为空,那么将下标0添加进链表中,然后将链表压入栈中;

当遍历到数组的1号位时,发现栈不为空,且因为arr[1]等于链表元素0所对应的arr[0],即都等于2,那么则将下标1添加进栈顶的链表中,如下图示:
单调栈结构(进阶)_第1张图片
接着数组遍历到2号时,发现arr[2]大于栈顶链表元素0所对应的arr[0],那么直接将下标2添加进新的链表中,然后将链表压入栈中。另外,当数组遍历到3号位时同当遍历到数组的1号位时的情况一样,如下图示:
单调栈结构(进阶)_第2张图片
当数组遍历到4号位时,发现arr[4]小于栈顶链表元素2所对应的arr[2],那么则将栈顶的链表弹出,然后遍历该链表,我们可以知道链表中的每个元素idx所对应的arr[idx]的左边和右边离其最近且小于arr[idx]的元素的下标分别为新的栈顶链表最右的元素(这里是3)当前遍历到的数组元素的下标(这里是4)。然后重复上述过程,直到arr[4]小于或等于新的栈顶链表元素idx所对应的arr[idx]才停止。

如果arr[4]等于新的栈顶链表元素idx所对应的arr[idx]时,那么就将arr[4]添加到该栈顶链表中,否则就将arr[4]添加进新的链表,然后再将该链表添加进栈中。由于arr[4]不等于栈顶链表元素2所对应的arr[2],那么我们将arr[4]添加进新的链表,然后再将该链表添加进栈中。如下图示:
单调栈结构(进阶)_第3张图片
接下来我们开始循环的弹出栈中的链表,然后遍历该链表,我们可以知道链表元素4所对应的arr[4]的左边离其最近且小于arr[4]的元素的下标为新的栈顶链表最右的元素的下标1,右边的则不存在,默认为-1。
单调栈结构(进阶)_第4张图片
我们继续弹出栈顶链表,然后遍历该链表。我们发现无新的栈顶,那么我们说明当前链表元素idx所对应的arr[idx]左边无离其最近且小于arr[idx]的元素,我们默认下标为-1,而右边的也不存在,默认为-1。
单调栈结构(进阶)_第5张图片

代码
import java.util.*;
import java.io.*;

public class Main {
	// 核心代码
    public static int[][] getNearLessRepeat(int[] arr) {
		ArrayDeque<List<Integer>> stack = new ArrayDeque<>();
		int[][] res = new int[arr.length][2];
		for (int i = 0; i < arr.length; i++) {
			while (!stack.isEmpty() && arr[i] < arr[stack.peek().get(0)]) {
				List<Integer> topList = stack.pop();
				// 不为空时为新的栈顶链表最右的元素
				int leftLessIdx = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
				for (Integer n : topList) {
					res[n][0] = leftLessIdx;
					res[n][1] = i;
				}
			}
			if (!stack.isEmpty() && arr[i] == arr[stack.peek().get(0)]) {
				stack.peek().add(i);
			} else {
				List<Integer> list = new ArrayList<>();
				list.add(i);
				stack.push(list);
			}
		}
		while (!stack.isEmpty()) {
			List<Integer> topList = stack.pop();
			int leftLessIdx = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
			for (Integer n : topList) {
				res[n][0] = leftLessIdx;
				res[n][1] = -1;
			}
		}
		return res;
	}
    
    // for test
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] arr = new int[n];
        for (int i = 0; i < n; i++) {
            arr[i] = sc.nextInt();
        }
        int[][] res = getNearLessRepeat(arr);
        for (int i = 0; i < n; i++) {
            System.out.println(res[i][0] + " " + res[i][1]);
        }
    }
}

你可能感兴趣的:(单调栈)