|
|
楼主 |
发表于 2025-8-20 21:24:03
|
显示全部楼层
1. 长度不超过 k 的最大子数组和
核心思想:结合前缀和与单调队列,将问题转化为 "在窗口内找到最小前缀和"
实现细节:
前缀和数组 prefix 表示前 i 个元素的和
单调队列维护前缀和的递增序列,确保能快速找到 prefix[j] - prefix 的最大值(i 和 j 的差不超过 k)
示例结果:数组 [1,-2,3,10,-4,7,2,-5] 中,长度不超过 4 的最大子数组和为 3+10-4+7=16
2. 任务调度中的资源峰值监控
核心场景:监控滑动窗口内的资源占用峰值,确保不超过系统阈值
实现细节:
单调队列(递减)维护窗口内的资源峰值
每次新任务加入时,移除窗口外的历史任务,并更新队列确保单调性
若任何窗口的峰值超过阈值,则返回调度失败
示例结果:资源序列 [3,1,4,2,5,3] 在窗口大小 3、阈值 4 时,因窗口 [4,2,5] 的峰值 5>4,返回不能调度
3. 0-1 BFS 优化(带权最短路径)
核心场景:求解网格中移动代价为 0 或 1 的最短路径(斜向移动代价 0,正交移动代价 1)
实现细节:
双端队列替代普通队列,代价 0 的节点从队头入队,代价 1 的节点从队尾入队
确保队列始终按距离递增,实现 O (n) 时间复杂度
示例结果:3x4 网格中从 (0,0) 到 (2,3) 的最短路径长度为 2(通过两次斜向移动)
4. 支持 O (1) 时间获取最大值的队列
核心功能:实现一个队列,支持入队、出队和获取最大值操作,均为 O (1) 时间
实现细节:
普通队列存储所有元素,单调队列(递减)维护最大值候选
入队时移除所有小于当前值的元素,出队时若弹出最大值则同步更新单调队列
示例结果:入队 3、1、4 后最大值为 4,出队 3 后最大值仍为 4,入队 2 后最大值保持 4,再出队 1 后最大值变为 2
总结
这些示例展示了单调队列在不同场景下的灵活性:
处理动态区间最值(如资源监控)
优化前缀和相关的子数组问题
加速特殊权重的图遍历(0-1 BFS)
实现高效的数据结构(带最大值的队列)
[C++] 纯文本查看 复制代码 #include <iostream>
#include <vector>
#include <deque>
#include <queue>
#include <climits>
#include <algorithm>
using namespace std;
/**
* 应用1:长度不超过k的最大子数组和
* 利用前缀和+单调队列,时间复杂度O(n)
*/
int maxSubarraySumWithMaxLength(const vector<int>& nums, int k) {
int n = nums.size();
if (n == 0 || k <= 0) return 0;
vector<long long> prefix(n + 1, 0); // 前缀和数组
for (int i = 0; i < n; ++i) {
prefix[i + 1] = prefix + nums;
}
deque<int> q; // 单调队列,存储前缀和索引,维持前缀和递增
int maxSum = INT_MIN;
for (int j = 0; j <= n; ++j) {
// 移除超出窗口范围的索引(窗口大小为j - i <= k)
while (!q.empty() && j - q.front() > k) {
q.pop_front();
}
// 当前前缀和 - 队列中最小前缀和 = 最大子数组和
if (!q.empty()) {
maxSum = max(maxSum, (int)(prefix[j] - prefix[q.front()]));
}
// 维护队列单调性:移除所有大于等于当前前缀和的元素
// 因为对于未来的j',prefix[j]比它们更小,是更优的选择
while (!q.empty() && prefix[j] <= prefix[q.back()]) {
q.pop_back();
}
q.push_back(j);
}
return maxSum;
}
/**
* 应用2:任务调度中的资源峰值监控
* 监控滑动窗口内的任务资源占用峰值,确保不超过阈值
*/
bool canScheduleTasks(const vector<int>& resources, int windowSize, int threshold) {
deque<int> maxQ; // 单调队列,维护窗口内资源占用峰值
for (int i = 0; i < resources.size(); ++i) {
// 移除超出窗口范围的元素
if (!maxQ.empty() && i - maxQ.front() >= windowSize) {
maxQ.pop_front();
}
// 维护队列单调性(递减)
while (!maxQ.empty() && resources >= resources[maxQ.back()]) {
maxQ.pop_back();
}
maxQ.push_back(i);
// 窗口形成后检查是否超过阈值
if (i >= windowSize - 1) {
if (resources[maxQ.front()] > threshold) {
return false; // 资源超限,无法调度
}
}
}
return true; // 所有窗口均满足条件
}
/**
* 应用3:0-1 BFS优化(求解带0/1权重的最短路径)
* 在网格中移动,上下左右移动代价为1,斜向移动代价为0,求最短路径
*/
int zeroOneBFS(const vector<vector<int>>& grid, pair<int, int> start, pair<int, int> end) {
int m = grid.size();
int n = grid[0].size();
const int INF = 1e9;
// 距离矩阵,初始化为无穷大
vector<vector<int>> dist(m, vector<int>(n, INF));
dist[start.first][start.second] = 0;
// 双端队列:代价0的节点从队头入队,代价1的节点从队尾入队
deque<pair<int, int>> dq;
dq.push_back(start);
// 8个方向:4个斜向(代价0),4个正交(代价1)
vector<pair<int, int>> dirs = {{-1,-1}, {-1,1}, {1,-1}, {1,1}, // 斜向,代价0
{-1,0}, {1,0}, {0,-1}, {0,1}}; // 正交,代价1
while (!dq.empty()) {
auto [x, y] = dq.front();
dq.pop_front();
// 到达终点,返回距离
if (x == end.first && y == end.second) {
return dist[x][y];
}
for (int i = 0; i < 8; ++i) {
int nx = x + dirs.first;
int ny = y + dirs.second;
// 检查边界和可访问性(假设grid[j]=1表示可访问)
if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] == 1) {
int cost = (i < 4) ? 0 : 1; // 前4个方向代价0,后4个代价1
int newDist = dist[x][y] + cost;
// 更新距离并加入队列
if (newDist < dist[nx][ny]) {
dist[nx][ny] = newDist;
if (cost == 0) {
dq.push_front({nx, ny}); // 代价0放队头
} else {
dq.push_back({nx, ny}); // 代价1放队尾
}
}
}
}
}
return -1; // 无法到达终点
}
/**
* 应用4:实现一个支持O(1)时间获取最大值的队列
*/
template <typename T>
class MaxQueue {
private:
queue<T> q; // 存储所有元素的普通队列
deque<T> maxDeque; // 单调队列,维护最大值
public:
// 入队操作
void push(T val) {
q.push(val);
// 移除队列中所有小于当前值的元素
while (!maxDeque.empty() && val > maxDeque.back()) {
maxDeque.pop_back();
}
maxDeque.push_back(val);
}
// 出队操作
void pop() {
if (q.empty()) return;
T front = q.front();
q.pop();
// 如果出队的是最大值,同步更新单调队列
if (front == maxDeque.front()) {
maxDeque.pop_front();
}
}
// 获取当前队列最大值
T getMax() {
if (maxDeque.empty()) {
throw runtime_error("Queue is empty");
}
return maxDeque.front();
}
// 判断队列是否为空
bool empty() {
return q.empty();
}
};
// 打印辅助函数
void printResult(int result, const string& msg) {
cout << msg << result << endl;
}
void printBool(bool result, const string& msg) {
cout << msg << (result ? "是" : "否") << endl;
}
int main() {
// 测试应用1:长度不超过k的最大子数组和
vector<int> nums1 = {1, -2, 3, 10, -4, 7, 2, -5};
int k1 = 4;
cout << "=== 应用1:长度不超过k的最大子数组和 ===" << endl;
cout << "数组: ";
for (int num : nums1) cout << num << " ";
cout << ", k=" << k1 << endl;
printResult(maxSubarraySumWithMaxLength(nums1, k1), "最大子数组和: ");
cout << endl;
// 测试应用2:任务调度资源监控
vector<int> resources = {3, 1, 4, 2, 5, 3};
int windowSize = 3, threshold = 4;
cout << "=== 应用2:任务调度资源监控 ===" << endl;
cout << "资源占用: ";
for (int r : resources) cout << r << " ";
cout << ", 窗口大小=" << windowSize << ", 阈值=" << threshold << endl;
printBool(canScheduleTasks(resources, windowSize, threshold), "是否能调度所有任务: ");
cout << endl;
// 测试应用3:0-1 BFS
vector<vector<int>> grid = {
{1, 1, 1, 1},
{1, 0, 0, 1},
{1, 1, 1, 1}
};
pair<int, int> start = {0, 0};
pair<int, int> end = {2, 3};
cout << "=== 应用3:0-1 BFS最短路径 ===" << endl;
cout << "网格大小: " << grid.size() << "x" << grid[0].size() << endl;
cout << "起点: (" << start.first << "," << start.second << "), 终点: (" << end.first << "," << end.second << ")" << endl;
printResult(zeroOneBFS(grid, start, end), "最短路径长度: ");
cout << endl;
// 测试应用4:带最大值的队列
cout << "=== 应用4:带最大值的队列 ===" << endl;
MaxQueue<int> mq;
mq.push(3);
mq.push(1);
mq.push(4);
cout << "入队3,1,4后,最大值: " << mq.getMax() << endl; // 4
mq.pop();
cout << "出队后,最大值: " << mq.getMax() << endl; // 4
mq.push(2);
cout << "入队2后,最大值: " << mq.getMax() << endl; // 4
mq.pop();
cout << "出队后,最大值: " << mq.getMax() << endl; // 2
return 0;
}
|
|