有关窗口的算法题

1. 生成窗口最大值数组

题目:有一个整型数组arr和一个大小为w的窗口从数组的最左边滑到最右边,窗口每次向右边滑一个位置。

例如,数组为【4,3,5,4,3,3,6,7】,窗口大小为3时:

窗口数组   最大值

[4 3 5] 4 3 3 6 7 max: 5

4 [3 5 4] 3 3 6 7 max: 5

4 3 [5 4 3] 3 6 7 max: 5

4 3 5 [4 3 3] 6 7 max: 4

4 3 5 4 [3 3 6] 7 max: 6

4 3 5 4 3 [3 6 7] max: 7

思路LinkedList为一个标准的双端队列,可以将其作为窗口。

1.1 窗口结构

窗口可以由双端队列实现。从队尾进,从队首出。有两个指针分别为head,tail,只增不减,代表窗口只往前走,不往后退。

  • 添加进窗口中的数据格式

    往窗口中添加的数据是[index, value],其中index表示该值是添加到该窗口的第几个元素。如果能通过index找到value的话,只往窗口中添加index即可。

  • 往窗口中添加数据

    先判断窗口中最后一个元素(即双端队列队尾元素)的值last是否不大于要加入的值cur,如果不大于,弹出last值。直到该窗口中最后的元素大于cur值。(也就是说即使队尾元素与cur相等,也要被踢出去。)

  • 删除窗口中的元素

    如果队首元素的index过期,就需要将其从队首弹出。

    所谓”过期“,就是窗口往前滑动的过程中,新的元素添加进来,旧的元素出去。通过当前将要添加到窗口的index值与队首第一个元素的index比较,如果相差大于窗口的容量,则队首元素过期。

1.2 代码

import java.util.LinkedList;

class Solution_MaxWindow{

    //w为窗口大小
    public static int[] getMaxWindow(int[] arr, int w){

        if(arr == null || w < 1){
            return null;
        }

        int len = arr.length;
        //用来存放每一个窗口中的最大值
        int[] maxs = new int[len - w + 1];
        //使用双端队列结构实现滑动窗口
        LinkedList<Integer> list = new LinkedList<Integer>();
        int index = 0;
        //遍历数组,将其存入窗口
        for(int i = 0; i < len; i++){
            //保证值最大的元素排在最首。
            while(list.peekLast() != null && arr[list.peekLast()] <= arr[i]){
                list.pollLast();
            }
            list.addLast(i);
			//过期条件如下
            if(i - list.peekFirst()  == w){
                list.pollFirst();
            }
            //从w-1开始,往maxs数组里添加窗口的最大值。
            if(i >= w - 1){
                maxs[index++] = arr[list.peekFirst()];
            }

        }
        return maxs;
    }

    public static void printArray(int[] arr){
        for(int value : arr){
            System.out.print(value + " ");
        }
        System.out.println();
    }

    public static void main(String[] args){
        int[] arr = new int[]{5,4,3,0,4,3,1,3,2,5};
        int[] maxs = getMaxWindow(arr, 3);
        printArray(maxs);
    }
}

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

题目

给定数组 arr 和整数 num, 共返回有多少个⼦数组满⾜如下情况:
max(arr[i…j]) - min(arr[i…j]) <= num
max(arr[i…j])表示⼦数组 arr[i…j]中的最⼤值,min(arr[i…j])表示⼦数组arr[i…j]中的最小值。

背景

  • 如果一个数组arr满足 最大值减去最小值小于等于num,那么这个数组的任意一个子数组也满足。
  • 如果一个数组arr不满足 最大值减去最小值小于等于num,那么再加上任意一个元素,依旧不满足。

思路

  1. 使用两个双端队列,一个用来存当前窗口的最大值,两一个用来存当前窗口的最小值。只不过窗口的容量是动态的。窗口的左右两个端点为leftright
  2. 遍历数组,每遍历一个数,分别得到当前窗口的最大值和最小值。
  3. 如果遇到一个数,使当前子数组不满足要求,此时包含当前窗口第一个元素的子数组都满足。则res += right - left
  4. 然后窗口左端点右移一位,即left++,然后接着遍历。
  5. 直到rightleft都移动到数组的最后一位。

:有数组[3,7,4,6,8]num=2,窗口滑动过程如下(为了便于观看,图中两个双端队列里的值为元素值,实际双端队列里存的是下标。)

  1. 第一步:窗口初始大小为1,最大值和最小值都为1,满足条件,
  2. 第二步:right加1,最大值跟改为7,最小值为3,不满足条件,此时窗口中子数组[3]满足条件,结果加1,left加1。
  3. 第三步:right加1,最大值仍旧为7,随着窗口的滑动,最小值变为4,不满足条件。此时窗口中子数组[7]满足条件,结果加1,left加1。
  4. 第四步:right加1,最大值为6,最小值变为4,满足条件。left不变。
  5. 第五步:right加1,最大值为8,最小值变为4,满足条件。left不变。
  6. 第六步:right已经到数组最右,此时窗口中子数组[4] [4,6] [4,6,8]满足条件,结果加3。left加1。
  7. 第七步:此时窗口中子数组[6]满足条件,结果加1。left加1。
  8. 第八步:此时窗口中子数组[8]满足条件,结果加1。left已到数组最右,结束。
    在这里插入图片描述
    代码
import java.util.LinkedList;

class Solution_GetSum2{

    public static int getSum(int[] arr, int num){

        if(arr == null || arr.length == 0 || num < 0){
            return 0;
        }
        //最后结果
        int result = 0;
        //实现两个窗口,分别记录窗口中的最大值和最小值。
        LinkedList<Integer> maxList = new LinkedList<Integer>();
        LinkedList<Integer> minList = new LinkedList<Integer>();
        int left = 0;
        int right = 0;
        while(left < arr.length){
            while(right < arr.length){
                //分别求出当前窗口中的最大值和最小值。
                while(!maxList.isEmpty() && arr[maxList.peekLast()] <= arr[right]){
                    maxList.pollLast();
                }
                while(!minList.isEmpty() && arr[minList.peekLast()] >= arr[right]){
                    minList.pollLast();
                }
                maxList.addLast(right);
                minList.addLast(right);
                if(arr[maxList.peekFirst()] - arr[minList.peekFirst()] > num){
                    break;
                }
                right++;
            }
            if(maxList.peekFirst() == left){
                maxList.pollFirst();
            }
            if(minList.peekFirst() == left){
                minList.pollFirst();
            }
            result += right - left;
            left++;
        }
        return result;
    }

    public static void main(String[] args){
        int[] arr = new int[]{3,7,4,6,8};
        System.out.println(getSum(arr, 2));
    }
}
发布了108 篇原创文章 · 获赞 22 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/GoSantiago/article/details/104832868