算法进阶(第二天)

介绍窗口以及窗口内最大值或最小值的更新结构(单调双向队列)

1. 往右走,一个数进来,如果大,那么就弹出前面的,直到前面的数比他大或者为空,然后加在后面,如果比前面的数小,则加在后面;

2. 往左走,判断队列的最前面的index是否过期,如果过期,则弹出。

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7

解法:

1.准备一个双端队列,向右走,如果比前面的大,则弹出前面的数,再添加到尾部,如果小,则直接添加到尾端

2. 如果index过期,则弹出顶部元素,此时栈顶元素为最大值。

public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length==0){
            return new int[0];
        }
        int index=0;
        int len = nums.length;
        LinkedList<Integer> list= new LinkedList<>();
        int[] res = new int[len-k+1];
        for(int i=0;i<len;i++){
            while(!list.isEmpty()&&nums[list.peekLast()]<nums[i]){
                list.pollLast();
            }
            list.offer(i);
            if(list.peekFirst()==i-k){
                list.pollFirst();
            }
            if(i>=k-1){
                res[index++] = nums[list.peekFirst()];
            }
        }

        return res;
    }

最大值减去最小值小于或等于num的子数组数量

给定数组arr和整数num,共返回有多少个子数组满足如下情况:

max(arr[i..j])-min(arr[i..j])<=num

max()表示子数组的最大值,min()表示子数组的最小值

解法:

1.准备两个双端队列,一个保存最大值,一个保存最小值

2.从左往右走,判断最大队列和最小队列顶部元素的差值,如果不满足,则退出;

3.然后左边继续走;

这样能够让所有的情况的保存下来;

    public int getNum(int[] nums, int num) {
        if(nums==null||nums.length){
            return 0;
        }
        LinkedList<Integer> qMax = new LinkedList<>();
        LinkedList<Integer> qMin = new LinkedList<>();
        int len = nums.length;
        int L = 0;
        int R = 0;
        int res = 0;
        while(L<len){
            while(R<len){
                while(!qMax.isEmpty()&&nums[qMax.peekLast()]<=nums[R]){
                    qMax.pollLast();
                }
                qMax.offerLast(R);

                while(!qMin.isEmpty()&&nums[qMin.peekLast()]>=nums[R]){
                    qMin.pollLast();
                }
                qMin.offerLast(R);
                
                if((nums[qMax.peekFirst()]-nums[qMin.peekFirst()])>num){
                    break;
                }
                R++;

            }
            res = res + R-L+1;
            if(qMax.peekFirst()==L){
                qMax.pollFirst();
            }
            if(qMin.peekFirst()==L){
                qMin.pollFirst();
            }
            L++;
        }
        return res;
    }

单调栈

1.准备一个堆栈,遍历整个数组,从左到右;

2.压栈,按照从大到小的顺序,如果遇到比栈顶元素大的话,则弹出栈顶元素;

   此元素是右边大的元素,栈下的左边大的元素;

3.如果相等的话,则将下标合并,遇到大的,弹出,遇到小的继续压栈;

4.压到最后,然后逐渐弹出,对于最后一个元素,右边没有比它大的,左边大的就是下面的元素;

构造数组的MaxTree

一个数组的MaxTree定义如下。

数组必须没有重复元素。

MaxTree是一棵二叉树,数组的每一个值对应一个二叉树的节点。

包括MaxTree树在内且在其中的每一棵子树上,值最大的节点都是树的头。

有重复元素的数组arr,写出生成这个数组的MaxTree的函数,要求如果数组长度为N,且空间复杂度为O(N)

解法1:利用大根堆来搞,弄好了再连接起来

解法2:利用单调栈

             1. 如果一个数的左右都没有大的数,那么此数作为根;

             2. 如果只有其中一个,那么此数直接挂在上面

             3. 如果有两个,那么选择小的,此数挂在小的上面

求最大子矩阵的大小

给定一个整型矩阵map,其中的值只有0和1两种,求其中全是1的所有矩阵中,最大的矩阵区域为1的数量

1.先讲解一个思路,将数组的数字想象成一个矩形;

2. 遍历数组,压栈,按照从小到大的顺序,遇到小的就弹出,遇到大的就继续加;

3. 遇到小的弹出时,如栈中是4,遇到3,要弹出4,此时3能够移动的距离就是2;

public static int maxRecFromBottom(int[] height){
        if(height==null||height.length==0){
            return 0;
        }
        int maxArea = 0;
        Stack<Integer> stack = new Stack<>();

        for(int i=0;i<height.length;i++){
            while(!stack.isEmpty()&&height[i]<=height[stack.peek()]){
                int j = stack.pop();
                int k = stack.isEmpty()?-1:stack.peek();
                int curArea = (i-k-1)*height[j];
                maxArea = Math.max(curArea,maxArea);
            }
            stack.push(i);
        }
        while(!stack.isEmpty()){
            int j = stack.pop();
            int k = stack.isEmpty()?-1:stack.peek();
            int curArea = (height.length-k-1)*height[j];
            maxArea = Math.max(curArea,maxArea);
        }
        return maxArea;
    }

4.那么回到原来的问题,我们想求一个矩阵中这样的情况,我们可以每一行进行处理;

    public static int maxRecSize(int[][] map) {
        if(map==null||map.length==0||map[0].length==0){
            return 0;
        }
        int maxArea = 0;
        int[] height = new int[map[0].length];
        for(int i=0;i<map.length;i++){
            for(int j=0;j<map[0].length;j++){
                height[j] = map[i][j]==0?0:height[j]+1;
            }
            maxArea = Math.max(maxArea,maxRecFromBottom(height));
        }
        return maxArea ;
    }

可见山峰问题

https://blog.csdn.net/pcwl1206/article/details/96841021#2.3%E3%80%81%E5%8F%AF%E8%A7%81%E5%B1%B1%E5%B3%B0%E9%97%AE%E9%A2%98

解法:先找到数组的最大值,然后利用单调栈,从小到大排序,遇到大的就弹出,

此时这个数的左右两个山峰都可以知道,为C(2,times) + 2*time

如果此时堆栈中只剩两个数,那么此时倒数第二个数是 C(2,times) + 2*time或者C(2,times) + 1*time

1的时候是最后一个数只有一个,2的时候是最后一个数只有两个

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值