📌  相关文章
📜  在 O(n) 时间内使用堆栈滑动窗口最大值(所有大小为 k 的子数组的最大值)

📅  最后修改于: 2022-05-13 01:57:09.122000             🧑  作者: Mango

在 O(n) 时间内使用堆栈滑动窗口最大值(所有大小为 k 的子数组的最大值)

给出一个包含N个整数和另一个整数k ≤ N的数组arr[] 。任务是找到每个大小为k的子数组的最大元素。

例子:

Input: arr[] = {9, 7, 2, 4, 6, 8, 2, 1, 5}
 k = 3
Output: 9 7 6 8 8 8 5
Explanation:
Window 1: {9, 7, 2}, max = 9
Window 2: {7, 2, 4}, max = 7
Window 3: {2, 4, 6}, max = 6
Window 4: {4, 6, 8}, max = 8
Window 5: {6, 8, 2}, max = 8
Window 6: {8, 2, 1}, max = 8
Window 7: {2, 1, 5}, max = 5

Input: arr[] = {6, 7, 5, 2, 1, 7, 2, 1, 10}
 k = 2
Output: 7 7 5 2 7 7 2 10
Explanation:
Window 1: {6, 7}, max = 7
Window 2: {7, 5}, max = 7
Window 3: {5, 2}, max = 5
Window 4: {2, 1}, max = 2
Window 5: {1, 7}, max = 7
Window 6: {7, 2}, max = 7
Window 7: {2, 1}, max = 2
Window 8: {1, 10}, max = 10

先决条件:下一个更大的元素

方法:
对于每个索引,计算子数组从该索引开始时当前元素最大的索引,即对于每个索引i ,索引j ≥ i使得max(a[i], a[i + 1], ... a[ j]) = a[i] 。让我们称之为max_upto[i]
然后从第 i索引开始的长度为k的子数组中的最大元素,可以通过检查从ii + k – 1的每个索引找到max_upto[i] ≥ i + k – 1 (最后一个索引那个窗口)。

堆栈数据结构可用于将值存储在窗口中,即最后访问或先前插入的元素将位于顶部(与当前元素具有最接近索引的元素)。

算法:

  1. 创建一个数组max_upto和一个堆栈来存储索引。将 0 压入堆栈。
  2. 运行从索引 1 到索引 n-1 的循环。
  3. 从堆栈中弹出所有索引,其中元素(array[s.top()])小于当前元素并更新max_upto[s.top()] = i – 1然后将 i 插入堆栈。
  4. 从堆栈中弹出所有索引并分配max_upto[s.top()] = n – 1
  5. 创建一个变量j = 0
  6. 运行一个从 0 到 n - k 的循环,循环计数器为 i
  7. 运行一个嵌套循环,直到 j < i 或 max_upto[j] < i + k – 1,在每次迭代中递增 j。
  8. 打印第 j 个数组元素。

执行:

C++
// C++ implementation of the approach
#include 
using namespace std;
 
// Function to print the maximum for
// every k size sub-array
void print_max(int a[], int n, int k)
{
    // max_upto array stores the index
    // upto which the maximum element is a[i]
    // i.e. max(a[i], a[i + 1], ... a[max_upto[i]]) = a[i]
 
    int max_upto[n];
 
    // Update max_upto array similar to
    // finding next greater element
    stack s;
    s.push(0);
    for (int i = 1; i < n; i++) {
        while (!s.empty() && a[s.top()] < a[i]) {
            max_upto[s.top()] = i - 1;
            s.pop();
        }
        s.push(i);
    }
    while (!s.empty()) {
        max_upto[s.top()] = n - 1;
        s.pop();
    }
    int j = 0;
    for (int i = 0; i <= n - k; i++) {
 
        // j < i is to check whether the
        // jth element is outside the window
        while (j < i || max_upto[j] < i + k - 1)
            j++;
        cout << a[j] << " ";
    }
    cout << endl;
}
 
// Driver code
int main()
{
    int a[] = { 9, 7, 2, 4, 6, 8, 2, 1, 5 };
    int n = sizeof(a) / sizeof(int);
    int k = 3;
    print_max(a, n, k);
 
    return 0;
}


Java
// Java implementation of the approach
import java.util.*;
 
class GFG
{
 
    // Function to print the maximum for
    // every k size sub-array
    static void print_max(int a[], int n, int k)
    {
        // max_upto array stores the index
        // upto which the maximum element is a[i]
        // i.e. max(a[i], a[i + 1], ... a[max_upto[i]]) = a[i]
 
        int[] max_upto = new int[n];
 
        // Update max_upto array similar to
        // finding next greater element
        Stack s = new Stack<>();
        s.push(0);
        for (int i = 1; i < n; i++)
        {
            while (!s.empty() && a[s.peek()] < a[i])
            {
                max_upto[s.peek()] = i - 1;
                s.pop();
            }
            s.push(i);
        }
        while (!s.empty())
        {
            max_upto[s.peek()] = n - 1;
            s.pop();
        }
        int j = 0;
        for (int i = 0; i <= n - k; i++)
        {
 
            // j < i is to check whether the
            // jth element is outside the window
            while (j < i || max_upto[j] < i + k - 1)
            {
                j++;
            }
            System.out.print(a[j] + " ");
        }
        System.out.println();
    }
 
    // Driver code
    public static void main(String[] args)
    {
        int a[] = {9, 7, 2, 4, 6, 8, 2, 1, 5};
        int n = a.length;
        int k = 3;
        print_max(a, n, k);
 
    }
}
 
// This code has been contributed by 29AjayKumar


Python3
# Python3 implementation of the approach
 
# Function to print the maximum for
# every k size sub-array
def print_max(a, n, k):
     
    # max_upto array stores the index
    # upto which the maximum element is a[i]
    # i.e. max(a[i], a[i + 1], ... a[max_upto[i]]) = a[i]
 
    max_upto=[0 for i in range(n)]
 
    # Update max_upto array similar to
    # finding next greater element
    s=[]
    s.append(0)
 
    for i in range(1,n):
        while (len(s) > 0 and a[s[-1]] < a[i]):
            max_upto[s[-1]] = i - 1
            del s[-1]
         
        s.append(i)
 
    while (len(s) > 0):
        max_upto[s[-1]] = n - 1
        del s[-1]
 
    j = 0
    for i in range(n - k + 1):
 
        # j < i is to check whether the
        # jth element is outside the window
        while (j < i or max_upto[j] < i + k - 1):
            j += 1
        print(a[j], end=" ")
    print()
 
# Driver code
 
a = [9, 7, 2, 4, 6, 8, 2, 1, 5]
n = len(a)
k = 3
print_max(a, n, k)
 
# This code is contributed by mohit kumar


C#
// C# implementation of the approach
using System;
using System.Collections.Generic;
 
class GFG
{
 
    // Function to print the maximum for
    // every k size sub-array
    static void print_max(int []a, int n, int k)
    {
        // max_upto array stores the index
        // upto which the maximum element is a[i]
        // i.e. max(a[i], a[i + 1], ... a[max_upto[i]]) = a[i]
 
        int[] max_upto = new int[n];
 
        // Update max_upto array similar to
        // finding next greater element
        Stack s = new Stack();
        s.Push(0);
        for (int i = 1; i < n; i++)
        {
            while (s.Count!=0 && a[s.Peek()] < a[i])
            {
                max_upto[s.Peek()] = i - 1;
                s.Pop();
            }
            s.Push(i);
        }
        while (s.Count!=0)
        {
            max_upto[s.Peek()] = n - 1;
            s.Pop();
        }
        int j = 0;
        for (int i = 0; i <= n - k; i++)
        {
 
            // j < i is to check whether the
            // jth element is outside the window
            while (j < i || max_upto[j] < i + k - 1)
            {
                j++;
            }
            Console.Write(a[j] + " ");
        }
        Console.WriteLine();
    }
 
    // Driver code
    public static void Main(String[] args)
    {
        int []a = {9, 7, 2, 4, 6, 8, 2, 1, 5};
        int n = a.Length;
        int k = 3;
        print_max(a, n, k);
 
    }
}
 
// This code has been contributed by 29AjayKumar


Javascript


输出:
9 7 6 8 8 8 5

复杂性分析:

  • 时间复杂度: O(n)。
    只需要遍历数组两次。所以时间复杂度是O(n)。
  • 空间复杂度: O(n)。
    需要两个大小为 n 的额外空间。

https://youtu.be/m

-Dqu7csdJk?list=PLqM7alHXFySEQDk2MDfbwEdjd2svVJH9p