Generate window maximum array

Posted by wakenbake on Mon, 10 Jan 2022 17:41:29 +0100

Generate the maximum array of windows (priority queue, monotone queue)

Problem Restatement:

Give you an integer array arr, with a sliding window of size w moving from the leftmost side of the array to the rightmost side of the array. You can only see k numbers in the sliding window. The sliding window moves only one bit to the right at a time. Returns the maximum value in the sliding window

Example 1:

Input: arr = [1,3,-1,-3,5,3,6,7], k = 3
 Output:[3,3,5,5,6,7]
Explanation:
Position of sliding window                Maximum
---------------               -----
[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

Problem analysis:

1. Violence solving: this problem can be solved by violence. When each window slides to the right, traverse the array once to get the maximum value array, but the time complexity is O (nk)
2. Priority queue: we can use to record the maximum value of each window. Each time we move the window to the right, we compare the current value with the previous maximum value, but this will cause a problem. When the maximum value of the previous window is at the leftmost end of the window, we will cause an error by sliding the window to the right. The maximum value may not exist in the current window, Therefore, every time we slide to the right, we judge the maximum value queue, judge whether the maximum value of the last queue currently stored is in the sliding window, delete it if it is not, add the new value and judge it (we can directly use the priority queue and set the sorting rules, so we don't need our own judgment when adding), The time complexity group of this method is O (nlogn)
3. Monotonic queue: create a queue, and judge the conditions each time you add it. If the corresponding value of the tail element of the queue is less than the current value to be added, delete the current tail element until it is greater than or equal to the current value, and add the corresponding element of the current value to the queue. Like the priority queue, we need to judge whether the corresponding value of the largest element is in the window, so we need to judge. Time complexity is O (N)
Problem solving method:

Violence solving, priority queue, monotone queue

Problem solving:

code:
package cn.basic.algorithm;

import java.util.LinkedList;
import java.util.PriorityQueue;

/**
 * @author FOLDN
 *  Generate window maximum array
 */
public class MaxWindow {
    public static void main(String[] args) {
        int[] arr = {1,3,-1,-3,5,3,6,7};
        int[] maxWindow = getMaxWindow1(arr, 3);
        for (int i = 0; i < maxWindow.length; i++) {
            System.out.print(maxWindow[i]);
        }
    }
    // Priority queue
    public static int[] getMaxWindow1(int[] arr,int w){
        if (arr == null || w <1 || arr.length < w){
            return null;
        }
        int[] maxArr = new int[arr.length - w +1];
        //Create priority queues and define comparison rules (using lambda expressions)
        PriorityQueue<int[]> pq = new PriorityQueue<>((q1,q2)->q1[0] != q2[0] ? q2[0] - q1[0]: q2[1] - q1[1]);
        //Compare the first w elements first
        for (int i = 0; i < w; i++) {
            pq.offer(new int[]{arr[i],i});
        }
        // Add the maximum value and subscript of the first w elements to the priority queue
        maxArr[0] = pq.peek()[0];
        for (int i = w; i < arr.length; i++) {
            // Queue the current element
            pq.offer(new int[]{arr[i],i});
            // Delete elements and subscripts that do not belong to the current window
            while (pq.peek()[1] <= i-w){
                pq.poll();
            }
            // Adds the maximum value to the maximum value array
            maxArr[i-w+1] = pq.peek()[0];
        }
        return maxArr;
    }
    // Monotone queue (optimal solution)
    public static int[] getMaxWindow2(int[] arr, int w){
        if (arr == null || w <1 || arr.length < w){
            return null;
        }
        int index = 0;
        int[] maxArr = new int[arr.length - w +1];
        LinkedList<Integer> qMax = new LinkedList<>();
        for (int i = 0; i < arr.length; i++) {
            // Move those that do not meet the conditions to c
            while (!qMax.isEmpty() && arr[qMax.peekLast()] < arr[i]){
                qMax.pollLast();
            }
            qMax.addLast(i);
            // To judge whether the element belongs to the window, the condition judgment here can also use the while loop
            if (qMax.peekFirst() == i - w){
                qMax.pollFirst();
            }
            
            if (i >= w -1){
                maxArr[index++] = arr[qMax.peekFirst()];
            }
        }
        return maxArr;
    }
}

Code parsing:

Priority queue:

First, the input value must meet the question conditions. If it does not meet the conditions, an exception will be thrown or a null value will be returned. We need to define an array to store the maximum value of each window. The size of the array is arr length - w+1. Create a priority queue and redefine the Comparator in the construction method to set the collation. First compare the first w elements, and then compare the first w elements. After adding new elements, judge whether the head element of the current queue belongs to the current window every time. If it is not satisfied, delete it until it is satisfied. Then you can add the value corresponding to the current queue header element to the maximum array (because the priority queue has been ordered)

Monotonic queue:

First, the input value must meet the question conditions. If it does not meet the conditions, an exception will be thrown or a null value will be returned. We need to define an array to store the maximum value of each window. The size of the array is arr length - w+1. Create a monotonic queue (the queue inherits the LindedList, so we use the LinkedList directly, too). Loop the array, update the monotonic queue before adding each element, always keep the current element to be added smaller than the current element, and then add the current element to the end of the queue. When the condition is not met, the tail element of the queue is deleted until the condition is met. After adding, remember to ensure that the current maximum element belongs to the current window.

Summary:

Priority queue: a data structure in Java, which can be used when we need to find the maximum and minimum value of an element by defining our own sorting rules (it will be sorted automatically when adding, and we don't need to sort).

Monotonic queue: the elements in the queue are sorted according to rules. If the pressed elements do not meet the rules, the end of the queue elements will pop up until the rules are met. (both the head and tail can be deleted, but only the tail can be added)

Extension: monotone stack

Monotone stack: the elements in the stack are sorted according to the rules. If the pressed elements do not meet the rules, the tail elements will pop up until the rules are met. (only the top of stack can be operated)

Topics: Java Algorithm