堆是一种集合,它总是知道自己最小的元素,并且能让你快速把这个元素取出来。它是优先队列的天然工具:一条等待队列,下一个被处理的不是等待最久的元素,而是最重要的元素。堆位于 heaps 词汇表中。
<min-heap> 构建一个空堆,最小的键会最先出来;<max-heap> 则是它的镜像。
<min-heap> ( -- min-heap )
<max-heap> ( -- max-heap )
每个元素都以一个值加一个键的形式存储,键就是 Factor 用来排序的优先级。heap-push 用来添加一个元素:
heap-push ( value key heap -- )
USING: heaps ;
<min-heap>
"alice" 5 pick heap-push ! value "alice", priority 5
"bob" 2 pick heap-push ! value "bob", priority 2
堆是可变的:heap-push 会就地修改它,而不是返回一个新的堆。heap-push 消耗掉堆之后,用 keep 可以把堆重新取回到栈上。
heap-peek 返回队首元素,也就是键最小的那个值,并且不会把它移除。heap-pop 既返回这个元素,又把它移除。heap-empty? 会报告是否还有元素剩下,这样你就知道什么时候该停止弹出。heap-pop-all 一次性移除所有元素,按优先级顺序返回一个由 { key value } 对组成的关联列表。
heap-peek ( heap -- value key )
heap-pop ( heap -- value key )
heap-empty? ( heap -- ? )
heap-pop-all ( heap -- alist )
USING: assocs heaps kernel prettyprint ;
<min-heap>
"alice" 5 pick heap-push
"bob" 2 pick heap-push
dup heap-empty? . ! => f (two items waiting)
dup heap-peek drop . ! => "bob" (smallest key, still in the heap)
dup heap-pop drop . ! => "bob" (smallest key, now removed)
heap-pop-all . ! => { { 5 "alice" } }
heap-pop 和 heap-empty? 是一次处理一个元素的基本构件:查看或弹出队首,对它进行操作,一旦 heap-empty? 返回 t 就停下来。下面的任务用 heap-pop-all 一步清空整个队列,但如果一直弹出直到堆为空,得到的顺序也是一样的。
heap-pop-all 返回的这些对是一个关联列表:values(位于 assocs 中)只保留值,丢弃键。
一家忙碌的银行按优先级为等候的顾客办理业务,而不是按他们到达的顺序。每位顾客取号时都会得到一个优先级数字,柜员接下来总是服务数字最小的那位(1最紧急)。最小堆正是最理想的等待队列。
定义 new-queue,它不接受任何输入,返回一个新的空优先队列。
new-queue
! => an empty priority queue
定义 join-queue,它接受顾客姓名、优先级数字和队列。它把顾客加入队列并返回该队列,这样就可以一个接一个地加入顾客。
new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue
! => the queue, now holding alice (5) and bob (2)
定义 next-name,它接受一个队列,返回下一位应该被服务的顾客姓名,也就是优先级数字最小的那位,并且不会将其移除。
new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue next-name .
! => "bob"
定义 serve-all,它接受一个队列,返回所有等候顾客的姓名,按他们应被服务的顺序排列。
new-queue "alice" 5 rot join-queue "bob" 2 rot join-queue
"carol" 9 rot join-queue serve-all .
! => { "bob" "alice" "carol" }