滑动窗口与单调栈题目

224 阅读2分钟

「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」。

前两篇介绍了滑动窗口与单调栈数据结构,接下来上题练习实操下。

滑动窗口数据结构介绍

单调栈数据结构介绍

一、滑动窗口题目

给定一个正整数数组arr,和一个整数num
某个arr中的子数组sub,如果想达标,必须满足:
sub中最大值 – sub中最小值 <= num,
返回arr中达标子数组的数量

解题前需要得出的2个结论:

image.png

public static int getNum(int[] arr, int num) {
    if (arr == null || arr.length == 0) {
        return 0;
    }
    LinkedList<Integer> qMin = new LinkedList<>();
    LinkedList<Integer> qMax = new LinkedList<>();
    int L = 0;
    int R = 0;
    int res = 0;
    while (L < arr.length) {
        while (R < arr.length) {
            // 小->大
            while (!qMin.isEmpty() && arr[qMin.peekLast()] >= arr[R]) {
                qMin.pollLast();
            }
            qMin.addLast(R);
            // 大->小
            while (!qMax.isEmpty() && arr[qMax.peekLast()] <= arr[R]) {
                qMax.pollLast();
            }
            qMax.addLast(R);
            if (arr[qMax.peekFirst()] - arr[qMin.peekFirst()] > num) {
                break;
            }
            R++;
        }
        res += R - L;
        if (qMin.peekFirst() == L) {
            qMin.pollFirst();
        }
        if (qMax.peekFirst() == L) {
            qMax.pollFirst();
        }
        L++;
    }
    return res;
}

二、单调栈题目

题目1

给定一个只包含正数的数组arr,arr中任何一个子数组sub,
一定都可以算出(sub累加和 )* (sub中的最小值)是什么,
那么所有子数组中,这个值最大是多少?

暴力求解

public static int max1(int[] arr) {
    int max = Integer.MIN_VALUE;
    int N = arr.length;
    for (int i = 0; i < N; i++) {
        for (int j = i; j < N; j++) {
            int minNum = Integer.MAX_VALUE;
            int sum = 0;
            for (int k = i; k <= j; k++) {
                sum += arr[k];
                minNum = Math.min(minNum, arr[k]);
            }
            max = Math.max(max, sum * minNum);
        }
    }
    return max;
}

利用单调栈求解

public static int max2(int[] arr) {
    int size = arr.length;
    int[] sums = new int[size];
    sums[0] = arr[0];
    for (int i = 1; i < size; i++) {
        sums[i] = sums[i - 1] + arr[i];
    }
    int max = Integer.MIN_VALUE;
    Stack<Integer> stack = new Stack<>();
    for (int i = 0; i < size; i++) {
        while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
            int j = stack.pop();
            max = Math.max(max, (stack.isEmpty() ? sums[i - 1] : (sums[i - 1] - sums[stack.peek()])) * arr[j]);
        }
        stack.push(i);
    }
    while (!stack.isEmpty()) {
        int j = stack.pop();
        max = Math.max(max, (stack.isEmpty() ? sums[size - 1] : (sums[size - 1] - sums[stack.peek()])) * arr[j]);
    }
    return max;
}

题目2

给定一个非负数组arr,代表直方图,返回直方图的最大长方形面积。

image.png

public static int largestRectangleArea(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    int maxArea = 0;
    Stack<Integer> stack = new Stack<>();
    int N = height.length;
    for (int i = 0; i < N; i++) {
        while (!stack.isEmpty() && height[stack.peek()] >= height[i]) {
            int j = stack.pop();
            int k = stack.isEmpty() ? -1 : stack.peek();
            int curArea = (i - k - 1) * height[j];
            maxArea = Math.max(maxArea, curArea);
        }
        stack.push(i);
    }
    while (!stack.isEmpty()) {
        int j = stack.pop();
        int k = stack.isEmpty() ? -1 : stack.peek();
        int curArea = (N - k - 1) * height[j];
        maxArea = Math.max(maxArea, curArea);
    }
    return maxArea;
}

三、总结

  1. 想用滑动窗口,要想办法把具体的问题转化为滑动窗口的处理流程
  2. 想用滑动窗口最值的更新结构,就看看处理流程下,是否需要最值这个信息
  3. 想用单调栈,要想办法把具体的问题转化为单调栈所解决的原问题
  4. 滑动窗口及其最大值和最小值的更新结构、单调栈,都是重要算法原型