LeetCode Actual Warfare: Maximum Sliding Window

Posted by friedice on Sun, 18 Aug 2019 11:31:27 +0200

Topic English

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7] 
Explanation: 

Window position                Max
---------------               -----
[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

Note:

You may assume k is always valid, 1 ≤ k ≤ input array's size for non-empty array.

Follow up:

Could you solve it in linear time?

Topic Chinese

Given an array nums, a sliding window of size k moves 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 one bit to the right at a time.

Returns the maximum value in the sliding window.

Examples:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
 Output: [3, 3, 5, 5, 6, 7] 
Explanation: 

  Maximum position of sliding window
---------------               -----
[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

Tips:

You can assume that k is always valid. If the input array is not empty, 1 < k < the size of the input array.

Advancement:

Can you solve this problem in linear time complexity?

Algorithmic Implementation

Realization 1: Violence to find the maximum value of each window.

public class Solution {
    public int[] MaxSlidingWindow(int[] nums, int k) {
        int len = nums.Length;
        if (len == 0)
            return nums;

        int[] result = new int[len - k + 1];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = GetMax(nums, i, i + k);
        }
        return result;        
    }
    
    public int GetMax(int[] arr,int start,int end)
    {
        int max = int.MinValue;
        for (int i = start; i < end; i++)
        {
            if (arr[i] > max)
                max = arr[i];
        }
        return max;
    }    
}

Realization 2: If you want to improve time efficiency, you need to use structure.

Structure of two-way queue

public class MyCircularDeque
{
    private int _pFront;
    private int _pRear;
    private readonly int[] _dataset;
    private int _length;
    private int _maxSize;

    /** Initialize your data structure here. Set the size of the deque to be k. */

    public MyCircularDeque(int k)
    {
        _dataset = new int[k];
        _length = 0;
        _maxSize = k;
        _pFront = 0;
        _pRear = 0;
    }

    /** Adds an item at the front of Deque. Return true if the operation is successful. */

    public bool InsertFront(int value)
    {
        if (_length == _maxSize)
            return false;

        _pFront = (_pFront - 1 + _maxSize) % _maxSize;
        _dataset[_pFront] = value;
        _length++;
        return true;
    }

    /** Adds an item at the rear of Deque. Return true if the operation is successful. */

    public bool InsertLast(int value)
    {
        if (_length == _maxSize)
            return false;

        _dataset[_pRear] = value;
        _pRear = (_pRear + 1) % _maxSize;
        _length++;
        return true;
    }

    /** Deletes an item from the front of Deque. Return true if the operation is successful. */

    public bool DeleteFront()
    {
        if (_length == 0)
            return false;
        _pFront = (_pFront + 1) % _maxSize;
        _length--;
        return true;
    }

    /** Deletes an item from the rear of Deque. Return true if the operation is successful. */

    public bool DeleteLast()
    {
        if (_length == 0)
            return false;
        _pRear = (_pRear - 1 + _maxSize) % _maxSize;
        _length--;
        return true;
    }

    /** Get the front item from the deque. */

    public int GetFront()
    {
        if (_length == 0)
            return -1;

        return _dataset[_pFront];
    }

    /** Get the last item from the deque. */

    public int GetRear()
    {
        if (_length == 0)
            return -1;
        int index = (_pRear - 1 + _maxSize) % _maxSize;
        return _dataset[index];

    }

    /** Checks whether the circular deque is empty or not. */

    public bool IsEmpty()
    {
        return _length == 0;
    }

    /** Checks whether the circular deque is full or not. */

    public bool IsFull()
    {
        return _length == _maxSize;
    }
}

code implementation

public class Solution {
    public int[] MaxSlidingWindow(int[] nums, int k) {
        int len = nums.Length;
        if (len == 0)
            return nums;
        int[] result = new int[len - k + 1];
        //Array positions that hold the current window maximum ensure that the values of array positions in the queue are sorted from large to small
        MyCircularDeque deque = new MyCircularDeque(len);

        for (int i = 0; i < len; i++)
        {
            // Guarantee from large to small, if the front number is small, it needs to pop up in turn until it meets the requirements.
            while (deque.IsEmpty() == false && nums[deque.GetRear()] <= nums[i])
            {
                deque.DeleteLast();
            }
            //Add array subscripts corresponding to the current value
            deque.InsertLast(i);

            // Determine whether the value of the current queue leader is valid
            if (deque.GetFront() <= i - k)
            {
                deque.DeleteFront();
            }

            // Save the maximum value in the current window when the window length is k
            if (i + 1 >= k)
            {
                result[i + 1 - k] = nums[deque.GetFront()];
            }
        }
        return result;        
    }
}

experimental result

Achievement 1:

  • Status: Through
  • 18/18 pass test cases
  • Execution time: 472 ms, beating 53.33% of all C # submissions
  • Memory consumption: 34.3 MB, beating 50.00% of all C # submissions

Achievement II:

  • Status: Through
  • 18/18 pass test cases
  • Execution time: 412 ms, defeating 82.76% of all C submissions
  • Memory consumption: 34.9 MB, beating 10.00% of all C submissions

Relevant pictures and texts: