这是一个非常经典的好问题!很多同学在初学时都会被这两个名字搞混,觉得它们都是为了“找最值”。

作为教练,我换个角度带你拆解一下。想象你在经营一家职业篮球队,现在你要筛选球员:

1. 优先队列(Priority Queue):只看实力的“功劳簿”

  • 逻辑:它不管球员是什么时候入队的,它只认“实力”。谁最强,谁就排在最前面。
  • 动作
    • 新球员进来:放到队里,按实力排个序(堆排序)。
    • 选人:永远选那个全队最强的。
  • 代价:因为每次都要在整组人里维护顺序,每次进人或出人都要 O(logn)O(\log n) 的时间。
  • 致命伤:它没有“过期”概念。一个 50 岁的老将如果实力依然是第一,他会永远霸占队首。但在 DP 中,我们经常要求“决策点必须在最近的 KK 步之内”,优先队列处理这种“滑动窗口”非常吃力。

2. 单调队列(Monotonic Queue):残酷的“竞技场”

  • 逻辑:它不仅看实力,还看**“潜力(年轻程度)”**。
  • 生存法则:如果一个新球员比你年轻(下标更大),而且比你还强(数值更大),那么你立刻就会被解雇,因为你存在的意义已经归零了——在未来的所有决策窗口里,新人都比你强,且比你活得久。
  • 动作
    • 从后门进(入队维护):新来的球员把队尾那些“年纪大且实力弱”的人全部踢走。
    • 从前门走(过期删除):检查队首的人是不是“太老了”(超出了滑动窗口范围),老了就踢掉。
  • 代价:虽然有踢人的动作,但每个元素最多进队一次、出队一次。平均下来,处理每个元素的代价是 O(1)O(1)
  • 核心优势:它是为**“滑动窗口”**而生的。

启发式对比总结

为了让你一眼看穿它们的本质区别,我们列一张表:

特性 优先队列 (Priority Queue) 单调队列 (Monotonic Queue)
底层结构 二叉堆 (Heap) 双端队列 (Deque)
时间复杂度 O(logn)O(\log n) O(1)O(1) (均摊)
关注点 全局最值 窗口最值
生存规则 强就行,不看年龄 又强又年轻才行
DP 场景 比如 Dijkstra、贪心优化 带长度限制的区间转移

教练的“觉醒提问”:

假设你在写一个 DP,状态转移方程是:

dp[i]=minj=iKi1{dp[j]}+cost[i]dp[i] = \min_{j=i-K}^{i-1} \{ dp[j] \} + cost[i]
  1. 如果你用 Priority Queue:你会把所有的 dp[j]dp[j] 丢进去。但当你走到 ii 时,你怎么快速删掉那个已经超出范围(下标小于 iKi-K)的 dp[j]dp[j] 呢?你可能得不停地检查堆顶,如果堆顶过期了就 Pop,直到堆顶没过期。虽然能做,但复杂度是 O(nlogn)O(n \log n)
  2. 如果你用 Monotonic Queue:你会发现,如果 dp[i1]<dp[i2]dp[i-1] < dp[i-2],那么 dp[i2]dp[i-2] 就彻底没用了(因为它比 i1i-1 弱,还比 i1i-1 先过期)。你只维护一个递增的队列,队首永远是窗口内最小的。复杂度是稳稳的 O(n)O(n)

一句话记住: 如果你只需要找一堆数里的最值,用 Priority Queue; 如果你要在一长串不停向前滑动的窗口里找最值,请务必使用 Monotonic Queue

听懂了这个“球员选拔”的逻辑吗?下次看到“长度为 KK 的区间”,你脑子里应该立刻弹出哪一个?

0 条评论

目前还没有评论...

信息

ID
19384
时间
1000ms
内存
256MiB
难度
10
标签
递交数
1
已通过
1
上传者