程序员代码面试指南第二版 8.单调栈结构(普通及进阶)

welcome to my blog

程序员代码面试指南第二版 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){
        //3 1 3 4 3 5 3 2 2
        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{
                //压入当前位置i后还要保证栈底到栈顶对应元素非递减
                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);
                }
                //不能保证的话,就要先弹出, 直到栈空或者能保证严格递增后再压入i
                else{//stackTop > curr
                    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)]);
                    }
                    //栈不为空 同时 curr和栈顶代表的元素相等, 将curr的索引加入的栈顶元素中
                    if(!s.isEmpty() && Integer.valueOf(arr[s.peek().get(0)]) == curr)
                        s.peek().add(i);
                    //否则的话就得新创建个ArrayList
                    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的那个索引!
                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]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值