使用单调栈解决力扣第42题--接雨水

1 单调栈

在解决这个问题前,我们先来了解一下,什么是单调栈

单调栈的概念比较简单,就是栈里面的元素要么是单调递增的,比如:1,3,5,7,9;要么是单调递减的,比如:9,6,5,4,3,2;要么是单调不增的,比如:9,9,6,6,3;要么是单调不降的,比如:1,1,2,3,3。

至于他是从栈顶到栈底,还是从栈底到栈顶,这个就不需要纠结了,而他的入栈与出栈和普通栈一样。

2 使用单调栈解决第一个问题-寻找一个数组中第i个元素前面比他大的第一个元素

假如说我们现在有一个数组为:9,2,8,5,4,6,7,现在我们要遍历整个数组,并且找到第i个元素左边比他大的第一个元素,比如第0个元素9,他的左边没有元素,所以他左边没有比他大的元素,所以对应的值一个为0(表示没有),再比如第5个元素6,他的左边有一个元素8比他大,所以他的返回值应该为3(因为0代表没有比他大的值,所以这里我们从1开始)。

那代码应该怎么写呢,容易想到的第一个算法应该就是暴力了吧,当我们循环到第i个元素时,再次循环第i-1~0个元素,找到一个比他大的值并且填充进去,代码可以这样写:

java 复制代码
public class Baoli {
    public static void main(String[] args) {
        int[] array = { 2,1,4,2,1,1,3,3,2,2};
        System.out.println(Arrays.toString(baoli(array)));
    }
    public static int[] baoli(int[] list) {
        int[] result = new int[list.length];
        result[0] = 0;
        for (int i = 1; i < list.length; i++) {
            int big=0;
            for (int j = i-1; j >= 0; j--) {
                if (list[j] > list[i]) {
                    big = j+1;
                    break;
                }
            }
            result[i] = big;
        }
        return result;
    }
}

对于以上代码我们再做些优化,当然效果是一样的:

java 复制代码
public static int[] baoli(int[] list) {
        int[] result = new int[list.length];
        result[0] = 0;
        int i = 1;
        int j = 0;
        while (i < list.length) {
            if(j==-1 || list[i]<list[j]) {
                result[i] = j+1;
                j=i++;
            }else j--;

        }
        return result;
    }

以上代码准确率毫无疑问是没有问题的,但从时间复杂复杂度方面考虑的话,他的时间复杂度为O(n^2),那有什么方法可以降低复杂段呢?

我们观察这组数据:9,2,8,5,4,6,7;如果我们找到了6这个元素所对应的值为8后,我们再去寻找7这个元素所对应的值时,我们观察方法,7是大于6的,而5与4皆小于6(因为查找6所对应的值时已经遍历过了他们两个),那我们可不可以跳过他们两个,直接去访问8这个元素呢?

这里我们可以引入一个栈来存放当前位置所对应的值的下标加一,因为我们还要考虑他左边没有比他更加大的值,所以初始化栈的时候,我们设栈顶指针为0,他所对应的栈顶元素为0,表示在左边已经没有比他更加大的值了,这个栈里面的元素是单调递增的,但数组所对应的栈元素是单调递减的,也就是说,当有一个元素比栈顶元素小时,我们就将这个元素所对应的下标+1入栈,当获取下一个元素时,就将元素直接与栈顶元素进行比较,如果栈顶元素比他大的话,说明这个元素所对应的值为当前栈顶元素所对应的值的索引位置,然后将该元素入栈;否则我们将栈顶元素抛出,然后继续比较,直到找到栈顶元素比它大或者栈顶元素为0,我们就将平方复杂度O(n^2)降低到了线性复杂度O(n),博主这里使用了一个数组模拟这个单调栈,可供大家进行参考:

java 复制代码
public static int[] monotonicStack(int[] array) {
        int[] result = new int[array.length];  //处之后的数组
        int[] stack = new int[array.length]; //单调栈

        int top=0;
        stack[top] = 0;
        int i=1; //array的下标
        int k=0; //result的下标
        while (k < array.length) {
            if(top==0 || array[i-1]<array[stack[top]-1] ){
                result[k++] = stack[top];
                stack[++top]=i++;
            }else top--;
        }
        return result;
    }

这样一看,他的时间复杂度直接从O(n^2)降到了O(n)对于博主我这个初学者来说,简直是完美

3 使用单调栈解决力扣第42题--接雨水

接下来我们就用单调栈来处理这个问题吧

根据题目,我们可以先确定,如果说柱子的个数小于3的话,那么他一定接不到雨水,所以当数组的长度小于3时,我们直接返回0就行。

然后我们再根据单调栈的学习,循环每个值,找到在他左边和在他右边第一个比他大的值,但这时我们忽略了一个问题,如果说在他左边(或右边)比他大的值时,是不是就意味着能接更多的雨水,所以在这里我们还需要寻找在他左边(或右边)比他大的值的更左边(或更右边)是否有更大的值,然后用两边最大的值中取较小的那个值(桶能接多少水取决于最短的木板)减去当前柱子的长度就可以得出当前柱子所能接水的最大容量,然后依次相加就可以得出结论。

以下是博主写的代码,可供大家参考:

java 复制代码
public class Test {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        String[] s=sc.nextLine().split(" ");
        int[] array =new int[s.length];
        for (int i = 0; i < s.length; i++) {
            array[i]=Integer.parseInt(s[i]);
        }
        System.out.println(trap(array));
    }

    public static int trap(int[] height) {
        if(height.length<3) return 0;
        int[] lList=lMax(height);
        int[] rList=rMax(height);

        int count = getCount(height, lList, rList);
        return count;
    }
    private static int getCount(int[] array, int[] lList, int[] rList) {
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            if (lList[i] != 0 && rList[i] != 0) {
                int lMax = lList[i];
                int rMax = rList[i];
                while (lList[lMax - 1] != 0) {
                    lMax = lList[lMax - 1];
                }
                while (rList[rMax - 1] != 0) {
                    rMax = rList[rMax - 1];
                }
                count += Math.min(array[rMax - 1], array[lMax - 1]) - array[i];
            }

        }
        return count;
    }

    public static int[] lMax(int[] array) {
        int[] result = new int[array.length];  //处之后的数组
        int[] stack = new int[array.length+10]; //单调栈

        int top = 0;
        stack[top] = 0;
        int i = 1; //array的下标
        int k = 0; //result的下标
        while (k < array.length) {
            if (top == 0 || array[i - 1] < array[stack[top] - 1]) {
                result[k++] = stack[top];
                stack[++top] = i++;
            } else top--;
        }
        return result;
    }

    public static int[] rMax(int[] array) {
        int[] result = new int[array.length];  //处之后的数组
        int[] stack = new int[array.length+10]; //单调栈

        int top = 0;
        stack[top] = 0;
        int i = array.length; //array的下标
        int k = array.length - 1; //result的下标
        while (k > -1) {
            if (top == 0 || array[i - 1] < array[stack[top] - 1]) {
                result[k--] = stack[top];
                stack[++top] = i--;
            } else top--;
        }
        return result;
    }
}

虽然说过程充满了坎坷,但数据总算全部通过了,啊!又是天才的一天。

相关推荐
dlraba8022 分钟前
机器学习-----K-means算法介绍
算法·机器学习·kmeans
Lisonseekpan1 小时前
MVCC的底层实现原理是什么?
java·数据库·后端·mysql
啊阿狸不会拉杆1 小时前
《算法导论》第 14 章 - 数据结构的扩张
数据结构·c++·算法·排序算法
闪电麦坤951 小时前
数据结构:栈(Stack)
数据结构
灰原喜欢柯南1 小时前
实战:MyBatis 中 db.properties 的正确配置与最佳实践
java·数据库·mybatis
中东大鹅2 小时前
SpringBoot实现文件上传
java·spring boot·后端
牛马程序员‍2 小时前
Day116 若依融合mqtt
java·mqtt·若依·mqttx
Q741_1472 小时前
如何判断一个数是 2 的幂 / 3 的幂 / 4 的幂 / n 的幂 位运算 总结和思考 每日一题 C++的题解与思路
开发语言·c++·算法·leetcode·位运算·总结思考
David爱编程2 小时前
Java中main 方法为何必须是static?
java·后端
小王爱学人工智能2 小时前
快速了解DBSCAN算法
算法·机器学习·支持向量机