剑指offer——滑动窗口的最大值(好题,数据结构)

来源:互联网 发布:预科生的贩毒网络豆瓣 编辑:程序博客网 时间:2024/05/18 20:11

题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:
假设n为数组长度,m为窗口的长度
蛮力法(O(nm))
可以用PriorityQueue实现一个最大堆(O(nmlogm)),或者直接冒泡法(O(n*m*m))


重温下PriorityQueue的使用(很傻的方法了)

import java.util.*;public class Solution {    public ArrayList<Integer> maxInWindows(int [] num, int size)    {        ArrayList<Integer> list = new ArrayList();        if(num==null||size>num.length||size==0)            return list;        PriorityQueue<Integer> maxHeap = new PriorityQueue(num.length, new Comparator<Integer>(){            public int compare(Integer o1, Integer o2) {            return o2-o1;        }        });        for(int i = 0; i<=num.length-size; i++){            maxHeap.clear();            for(int j = i; j<i+size; j++){                maxHeap.offer(num[j]);            }            list.add(maxHeap.peek());        }        return list;    }}

其实也可以用一个队列来保存最大值,参考栈的min函数那道题。


剑指offer思路:双端队列

/**
用一个双端队列,队列第一个位置始终保存当前窗口的最大值,当窗口滑动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
*/

这里写图片描述

import java.util.*;public class Solution {   public ArrayList<Integer> maxInWindows(int [] num, int size)    {        ArrayList<Integer> res = new ArrayList<>();         if(num==null||size>num.length||size == 0) return res;         int begin;          ArrayDeque<Integer> q = new ArrayDeque<>(); // 声明一个双端队列,队列中存储的是下标值!        for(int i = 0; i < num.length; i++){            begin = i - size + 1; // 滑动窗口起始元素的下标值            if(q.isEmpty())                q.add(i); // 将数据插在队尾            // 如果begin比队列的头值大,说明这个头值已失效(在窗口外);            //只要q不为空,则每次都会进行头值有效性判断            else if(begin > q.peekFirst())                 q.pollFirst();            while((!q.isEmpty()) && num[q.peekLast()] <= num[i]) //与即将加入的元素进行比较,把队尾中不可能成为最大值候选的元素全部删除                q.pollLast();            q.add(i);               if(begin >= 0)                res.add(num[q.peekFirst()]);        }        return res;    }}

直接的思路

    /*      * 找出每个窗口里的最大值,每次只要判断最大值是否超出范围,以及新加入的值是否比该值更大      */      public ArrayList<Integer> maxInWindows(int[] num, int size) {          ArrayList<Integer> result = new ArrayList<Integer>();          if (num == null || size <= 0 || num.length < size)              return result;          int max = findMaxIndex(num, size, 0);          result.add(num[max]);          for (int i = size; i < num.length; i++) {              if (num[max] < num[i])                  max = i;              // 如果max不在滑动窗口范围内,重新找最大值              if (max <= i - size) {                  max = findMaxIndex(num, size, i - size + 1);              }              result.add(num[max]);          }          return result;      }      /*      * 查找num指定范围内的最大值的下标      */      private int findMaxIndex(int[] num, int size, int begin) {          int max = begin;          int end = (begin + size) > num.length ? num.length : (begin + size);          for (int i = begin; i < end; i++) {              if (num[max] < num[i]) {                  System.out.println("num[max]:" + num[max] + " " + "num[i]:" + num[i]);                  max = i;                  System.out.println("max=" + max);              }          }          return max;      }  
原创粉丝点击