@[TOC]([仓颉Cangjie刷题模板] 优先队列(含小顶堆实现) )
一、 算法&数据结构
1. 描述
堆是一个可以维护实时最大/最小值的数据结构,相比treeset等常数优很多。
常用于维护一组数据的极值贪心问题。
2. 复杂度分析
- 初始化O(n)
- 查询O(1)
- 修改O(lgn)
3. 常见应用
- Dijkstra
- 丑数II
- 3D接雨水
4. 常用优化
- 本文模板实现的是小顶堆,要用大顶堆可以取反。
- 注意经常要用的元组,在仓颉里不能比大小,因此可以用数组代替,并实现扩展排序(但是很麻烦);或者自己实现元组(pair等)
二、 模板代码
1. 丑数
例题: 264. 丑数 II
按照从小到大的顺序枚举每个丑数可以派生的丑数,那么第k次出堆的那个就是答案。
extend<T> Array<T> where T <: Less<T> {operator func <(rhs: Array<T>): Bool {for (i in 0..min(size, rhs.size)) {if (this[i] < rhs[i]) {return true}}return this.size < rhs.size}// public func equals(other: Array<T>){// return true// }
}
class MinHeap<T> <: Collection<T> where T <: Less<T> {public prop size: Int {get() {h.size}}protected var h = ArrayList<T>()public init() { } public init(items: Collection<T>) {h.appendAll(items)for (i in (h.size/2)..=0:-1) {_siftup(i)} }public func isEmpty() {return h.isEmpty()}public func iterator() {throw Exception("No Iterator for MinHeap")// return h.iterator()}public func heappush(v:T) {h.append(v)var i = h.size - 1while (i > 0 && v < (h[(i-1) >> 1])) { // 比父节点小,则上移h[i] = h[(i-1) >> 1]i = (i-1) >> 1}h[i] = v}public func heappop() {// let last = h.remove(h.size-1)// if (h.size == 0) {// return last// }// let ret = h[0]// h[0] = last // _siftup(0)// return retlet ret = h[0]h[0] = h[h.size-1]h.remove(h.size-1)if (!h.isEmpty()) {_siftup(0)}return ret}public func heapreplace(v:T) { // 替换并返回堆顶let ret = h[0]h[0] = v _siftup(0)return ret}public func heappushpop(v:T) { // 把v和堆顶保留更大的那个,pop返回更小的那个;常用语保留最大的k个值(小顶堆)if (!h.isEmpty() && h[0] < v) {let ret = h[0]h[0] = v return ret} return v}private func _siftup(i:Int64){// 把i位置的大值下沉,小儿子提上来var fa = ilet v = h[fa]let n = h.size while ( (fa << 1 | 1) < n) { // 有左儿子var child = fa << 1 | 1 let right = child + 1if (right < n && h[right] < h[child]) {child = right }if (h[child] < v) {h[fa] = h[child] // 小的儿子上移fa = child // 迭代} else {break}}if (fa != i) { // 有变化h[fa] = v}}
}
extend<T> MinHeap<T> <: ToString where T <: ToString {public func toString() { return h.toString() }
}
public class Solution {public func nthUglyNumber(n: Int64): Int64 {let h = MinHeap<Int64>([1])let vis = HashSet<Int64>();let q = [2,3,5]for (_ in 0..n-1) {let v = h.heappop()for (i in q) {let p = i*vif (vis.contains(p)) {continue}vis.put(p)h.heappush(p)}}return h.heappop()}
}
2. Dijkstra模板体
例题: 743. 网络延迟时间
跑一遍Dij,距离最大的就是答案
import std.collection.*
import std.math.*class MinHeap<T> <: Collection<T> where T <: MyLess<T> {public prop size: Int {get() {h.size}}protected var h = ArrayList<T>()public init() { } public init(items: Collection<T>) {h.appendAll(items)for (i in (h.size/2)..=0:-1) {_siftup(i)} }public func isEmpty() {return h.isEmpty()}public func iterator() {throw Exception("No Iterator for MinHeap")// return h.iterator()}public func heappush(v:T) {h.append(v)var i = h.size - 1while (i > 0 && v < (h[(i-1) >> 1])) { // 比父节点小,则上移h[i] = h[(i-1) >> 1]i = (i-1) >> 1}h[i] = v}public func heappop() {// let last = h.remove(h.size-1)// if (h.size == 0) {// return last// }// let ret = h[0]// h[0] = last // _siftup(0)// return retlet ret = h[0]h[0] = h[h.size-1]h.remove(h.size-1)if (!h.isEmpty()) {_siftup(0)}return ret}public func heapreplace(v:T) { // 替换并返回堆顶let ret = h[0]h[0] = v _siftup(0)return ret}public func heappushpop(v:T) { // 把v和堆顶保留更大的那个,pop返回更小的那个;常用语保留最大的k个值(小顶堆)if (!h.isEmpty() && h[0] < v) {let ret = h[0]h[0] = v return ret} return v}private func _siftup(i:Int64){// 把i位置的大值下沉,小儿子提上来var fa = ilet v = h[fa]let n = h.size while ( (fa << 1 | 1) < n) { // 有左儿子var child = fa << 1 | 1 let right = child + 1if (right < n && h[right] < h[child]) {child = right }if (h[child] < v) {h[fa] = h[child] // 小的儿子上移fa = child // 迭代} else {break}}if (fa != i) { // 有变化h[fa] = v}}
}
interface MyLess<T> {operator func <(rhs: T): Bool
}
extend<T> Array<T> <: MyLess<Array<T>> where T <: Less<T> { public operator func <(rhs: Array<T>): Bool {for (i in 0..min(size, rhs.size)) {if (this[i] < rhs[i]) {return true} else if (rhs[i] < this[i]) {return false}}return this.size < rhs.size}// public func equals(other: Array<T>){// return true// }
}
extend<T> MinHeap<T> <: ToString where T <: ToString {public func toString() { return h.toString() }
}public class Solution {public func networkDelayTime(times: Array<Array<Int64>>, n: Int64, k: Int64): Int64 {let g = Array(n, {_=>ArrayList<(Int64,Int64)>()})for (e in times) {let (u,v,w) = (e[0],e[1],e[2])g[u-1].append((v-1,w))}let dis = Array(n, {_=>Int64.Max})dis[k-1] = 0let h = MinHeap([[0,k-1]])while (!h.isEmpty()) {let arr = h.heappop()let (c, u) = (arr[0],arr[1])if (c > dis[u]) {continue}for (e in g[u]){let (v, w) = (e[0],e[1])if (c + w < dis[v]) {dis[v] = c+w h.heappush([c+w,v])}}}let ans = max(dis).getOrThrow()return if (ans == Int64.Max){-1} else {ans}}
}
三、其他
- 堆本质是一种贪心,因此在很多场景都有应用。比如区间离线问题
四、更多例题
五、参考链接
- [python刷题模板] 单调队列