import java.util.*;

/**
 * NC82 滑动窗口的最大值
 * @author d3y1
 */
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     *
     * @param num int整型一维数组
     * @param size int整型
     * @return int整型ArrayList
     */
    public ArrayList<Integer> maxInWindows (int[] num, int size) {
        // return solution1(num, size);
        // return solution2(num, size);
        return solution3(num, size);
    }

    /**
     * 双指针+TreeMap
     * @param num
     * @param size
     * @return
     */
    private ArrayList<Integer> solution1(int[] num, int size){
        int n = num.length;
        if(n<size || size==0){
            return new ArrayList<>();
        }

        // TreeMap<Integer,Integer> map = new TreeMap<>((o1,o2) -> (o2-o1));
        // TreeMap<Integer,Integer> map = new TreeMap<>(Comparator.comparing(o -> o, Comparator.reverseOrder()));
        TreeMap<Integer,Integer> map = new TreeMap<>(new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2){
                return o2-o1;
            }
        });
        ArrayList<Integer> list = new ArrayList<>();

        // 双指针
        int i = 0;
        int j = 0;
        while(j < size){
            map.put(num[j], map.getOrDefault(num[j], 0)+1);
            j++;
        }
        list.add(map.firstKey());

        int numL,numR;
        while(j < n){
            numL = num[i++];
            numR = num[j++];
            map.put(numL, map.get(numL)-1);
            if(map.get(numL) == 0){
                map.remove(numL);
            }
            map.put(numR, map.getOrDefault(numR, 0)+1);
            list.add(map.firstKey());
        }

        return list;
    }

    /**
     * 大根堆(优先队列)
     * @param num
     * @param size
     * @return
     */
    private ArrayList<Integer> solution2(int[] num, int size){
        int n = num.length;
        if(n<size || size==0){
            return new ArrayList<>();
        }

        // 大根堆(优先队列)
        // PriorityQueue<Node> maxHeap = new PriorityQueue<>((o1,o2) -> (o2.num-o1.num));
        // PriorityQueue<Node> maxHeap = new PriorityQueue<>(Comparator.comparing(o -> o.num, Comparator.reverseOrder()));
        // PriorityQueue<Node> maxHeap = new PriorityQueue<>(Comparator.comparing(Node::getNum, Comparator.reverseOrder()));
        // PriorityQueue<Node> maxHeap = new PriorityQueue<>(Comparator.comparingInt(Node::getNum).reversed());
        PriorityQueue<Node> maxHeap = new PriorityQueue<>(new Comparator<Node>(){
            @Override
            public int compare(Node o1, Node o2){
                return o2.num-o1.num;
            }
        });
        ArrayList<Integer> list = new ArrayList<>();

        for(int i=0; i<n; i++){
            maxHeap.offer(new Node(num[i], i));

            // 当前窗口之外(左端越界)
            while(!maxHeap.isEmpty() && maxHeap.peek().idx<=i-size){
                maxHeap.poll();
            }
            // 能够形成窗口
            if(i+1 >= size){
                list.add(maxHeap.peek().num);
            }
        }

        return list;
    }

    /**
     * 单调栈(双端队列)
     * @param num
     * @param size
     * @return
     */
    private ArrayList<Integer> solution3(int[] num, int size){
        int n = num.length;
        if(n<size || size==0){
            return new ArrayList<>();
        }

        // Deque<Integer> stack = new ArrayDeque<>();
        Deque<Integer> stack = new LinkedList<>();
        ArrayList<Integer> list = new ArrayList<>();

        // 单调栈 双端队列
        for(int i=0; i<n; i++){
            // 单调减
            while(!stack.isEmpty() && num[stack.peekLast()]<=num[i]){
                stack.pollLast();
            }
            stack.offerLast(i);

            // 当前窗口之外(左端越界)
            if(stack.peekFirst() <= i-size){
                stack.pollFirst();
            }
            // 能够形成窗口
            if(i+1 >= size){
                list.add(num[stack.peekFirst()]);
            }
        }

        return list;
    }

    private class Node {
        private int num;
        private int idx;

        private int getNum(){
            return num;
        }

        private Node(int num, int idx){
            this.num = num;
            this.idx = idx;
        }
    }
}