每日一题——滑动窗口的最大值

滑动窗口的最大值

题目描述

给定一个长度为 n 的数组 num 和滑动窗口的大小 size,找出所有滑动窗口里数值的最大值。

例如,如果输入数组 {2, 3, 4, 2, 6, 2, 5, 1} 及滑动窗口的大小 3,那么一共存在 6 个滑动窗口,它们的最大值分别为 {4, 4, 6, 6, 6, 5}

示例

示例 1

输入:
[2,3,4,2,6,2,5,1], 3

返回值:
[4, 4, 6, 6, 6, 5]

示例 2

输入:
[9,10,9,-7,-3,8,2,-6], 5

返回值:
[10, 10, 9, 8]

示例 3

输入:
[1, 2, 3, 4], 5

返回值:
[]

说明

  • 如果滑动窗口的大小大于数组的长度或者大小为 0,则返回空数组。

解题思路

这个问题可以通过滑动窗口的方式解决,同时需要注意效率和空间复杂度。我们可以使用 双端队列(Deque) 来高效地解决这个问题。

双端队列的特点

  • 双端队列可以在 O(1) 的时间复杂度内从两端删除和添加元素。
  • 通过双端队列,我们可以在滑动窗口内保持一个有序的队列,其中队列的前端始终是窗口中的最大值。

实现步骤

  1. 初始化双端队列:用于保存当前滑动窗口内的元素索引,队列保持单调递减的顺序,即队列中的元素从前到后的值是递减的。
  2. 遍历数组 :每遍历到一个新的元素时:
    • 移除不在窗口内的元素。
    • 维护队列的递减性质,移除队列中比当前元素小的所有元素。
    • 将当前元素的索引添加到队列中。
    • 当窗口大小达到 size 时,队列前端的元素就是当前窗口的最大值。
  3. 返回结果:每次更新窗口后,将最大值添加到结果数组中。

代码实现(C语言)

c 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    int *data;  // 用于存储队列元素的数组
    int front;  // 队列头
    int rear;   // 队列尾
} Deque;

// 创建一个空的双端队列
Deque* createDeque(int capacity) {
    Deque *dq = (Deque*)malloc(sizeof(Deque));
    dq->data = (int*)malloc(capacity * sizeof(int));
    dq->front = -1;
    dq->rear = -1;
    return dq;
}

// 判断队列是否为空
int isEmpty(Deque *dq) {
    return dq->front == -1;
}

// 判断队列是否已满
int isFull(Deque *dq) {
    return dq->rear == dq->front - 1;
}

// 从队列前端删除元素
void popFront(Deque *dq) {
    if (!isEmpty(dq)) {
        dq->front++;
        if (dq->front > dq->rear) {
            dq->front = dq->rear = -1;  // 队列空时重置
        }
    }
}

// 从队列后端删除元素
void popBack(Deque *dq) {
    if (!isEmpty(dq)) {
        dq->rear--;
        if (dq->rear < dq->front) {
            dq->front = dq->rear = -1;  // 队列空时重置
        }
    }
}

// 从队列前端获取元素
int front(Deque *dq) {
    return dq->data[dq->front];
}

// 向队列后端添加元素
void pushBack(Deque *dq, int value) {
    if (dq->front == -1) {
        dq->front = dq->rear = 0;
    } else {
        dq->rear++;
    }
    dq->data[dq->rear] = value;
}

// 滑动窗口最大值的函数
int* maxSlidingWindow(int* nums, int numsSize, int size, int* returnSize) {
    int *result = (int*)malloc((numsSize - size + 1) * sizeof(int));
    *returnSize = 0;

    if (size == 0) {
        return result;  // 如果窗口大小为0,返回空数组
    }

    Deque *dq = createDeque(numsSize);  // 创建双端队列

    for (int i = 0; i < numsSize; ++i) {
        // 移除队列中超出窗口范围的元素
        if (!isEmpty(dq) && front(dq) < i - size + 1) {
            popFront(dq);
        }

        // 移除队列中比当前元素小的元素
        while (!isEmpty(dq) && nums[front(dq)] <= nums[i]) {
            popBack(dq);
        }

        // 添加当前元素到队列
        pushBack(dq, i);

        // 当窗口大小达到 size 时,记录当前窗口的最大值
        if (i >= size - 1) {
            result[(*returnSize)++] = nums[front(dq)];
        }
    }

    return result;
}

// 测试函数
int main() {
    int nums[] = {2, 3, 4, 2, 6, 2, 5, 1};
    int size = 3;
    int returnSize = 0;

    int *result = maxSlidingWindow(nums, sizeof(nums) / sizeof(nums[0]), size, &returnSize);

    for (int i = 0; i < returnSize; ++i) {
        printf("%d ", result[i]);
    }

    free(result);  // 释放结果数组
    return 0;
}

代码解析

  1. 初始化双端队列

    • 我们使用结构体 Deque 来表示双端队列。队列包含一个动态分配的数组 data 来存储元素,以及 frontrear 来表示队列的前端和后端。
    • createDeque 函数用于创建一个空的双端队列。
    • popFrontpopBack 分别是从队列的前端和后端删除元素。
    • pushBack 是向队列后端添加元素。
    • front 函数返回队列前端的元素。
  2. 滑动窗口的最大值函数

    • maxSlidingWindow 函数是核心逻辑,接收一个数组、窗口大小和数组的大小,并返回一个数组,表示每个滑动窗口的最大值。
    • 使用双端队列来维护当前窗口的最大值。每次窗口滑动时,移除不再窗口范围内的元素,并维护队列的单调性,确保队列中的最大元素总是在队列的前端。
  3. 时间与空间复杂度

    • 时间复杂度 :每个元素至多被添加和移除一次,所以时间复杂度是 O(n),其中 n 是数组的长度。
    • 空间复杂度 :队列最多存储 n 个元素,因此空间复杂度是 O(n)

总结

捣鼓了半天的数组和堆栈,结果发现实现不了,pop和push是最难的,所以还是要老老实实根据教程案例来学习,标新立异没必要。

相关推荐
IT猿手22 分钟前
2025最新群智能优化算法:海市蜃楼搜索优化(Mirage Search Optimization, MSO)算法求解23个经典函数测试集,MATLAB
开发语言·人工智能·算法·机器学习·matlab·机器人
马剑威(威哥爱编程)25 分钟前
C语言操作MySQL从入门到精通
c语言·mysql·adb
IT猿手2 小时前
2025最新群智能优化算法:山羊优化算法(Goat Optimization Algorithm, GOA)求解23个经典函数测试集,MATLAB
人工智能·python·算法·数学建模·matlab·智能优化算法
Dream it possible!5 小时前
LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)
c++·算法·leetcode
Kurbaneli6 小时前
深入理解 C 语言函数的定义
linux·c语言·ubuntu
Archer1946 小时前
C语言——链表
c语言·开发语言·链表
修己xj7 小时前
算法系列之深度优先搜索寻找妖怪和尚过河问题的所有方式
算法
夜晚中的人海7 小时前
【C语言】------ 实现扫雷游戏
android·c语言·游戏
面会菜.7 小时前
C语言(队列)
c语言·开发语言
开心比对错重要7 小时前
leetcode69.x 的平方根
数据结构·算法·leetcode