Data structures 二项式堆的融合/合并应该一次完成还是两次完成?

Data structures 二项式堆的融合/合并应该一次完成还是两次完成?,data-structures,functional-programming,binomial-heap,Data Structures,Functional Programming,Binomial Heap,Okasaki在纯功能数据结构中的实现(第22页)分为两种:一种是合并森林,另一种是传播载体。这让我觉得比一次性版本更难分析,也可能更慢。我错过什么了吗 冈崎的实施: functor BinomialHeap (Element:ORDERED):HEAP= struct structure Elem=Element datatype Tree = Node of int*Elem.T*Tree list type Heap = Tree list fun link (t1 as

Okasaki在纯功能数据结构中的实现(第22页)分为两种:一种是合并森林,另一种是传播载体。这让我觉得比一次性版本更难分析,也可能更慢。我错过什么了吗

冈崎的实施:

functor BinomialHeap (Element:ORDERED):HEAP= struct structure Elem=Element datatype Tree = Node of int*Elem.T*Tree list type Heap = Tree list fun link (t1 as Node (r,x1,c1), t2 as Node (_,x2,c2))= if Elem.leq(x1,x2) then Node (r+1,x1,t2::c1) else Node (r+1,x2,t1::c2) fun insTree (t,[])=[t] |insTree (t,ts as t'::ts')= if rank t < rank t' then t::ts else insTree(link(t,t'),ts') fun insert (x,ts)=insTree(Node(0,x,[]),ts) (*just for reference*) fun merge (ts1,[])=ts1 |merge ([],ts2)=ts2 |merge (ts1 as t1::ts1', ts2 as t2:ts2')= if rank t1 < rank t2 then t1::merge(ts1',ts2) else if rank t2 < rank t1 then t2::merge(ts1,ts2') else insTree (link(t1,t2), merge (ts1',ts2')) end 函子BinomialHeap(元素:有序):HEAP= 结构 结构元素=元素 数据类型树=int*Elem.T*树列表的节点 类型Heap=树列表 有趣的链接(t1作为节点(r,x1,c1),t2作为节点(x2,c2))= 如果元素leq(x1,x2) 然后节点(r+1,x1,t2::c1) else节点(r+1,x2,t1::c2) 趣味树(t,[])=[t] |insTree(t,ts作为t’::ts’)= 如果秩t<秩t',则t::ts else insTree(link(t,t'),ts')) 有趣的插入(x,ts)=insTree(节点(0,x,[]),ts)(*仅供参考*) 有趣的合并(ts1,[])=ts1 |合并([],ts2)=ts2 |合并(ts1作为t1::ts1',ts2作为t2:ts2')= 如果秩t1<秩t2,则t1::merge(ts1',ts2) 否则,如果秩t2<秩t1,则t2::merge(ts1,ts2') else insTree(链接(t1,t2),合并(ts1',ts2')) 结束 这让我觉得很难分析,因为你必须证明传播所有载体的成本上限(见下文)。我提出的自顶向下的合并实现更明显地是O(logn),其中n是较大堆的大小:

functor BinomialHeap (Element:ORDERED):HEAP= struct structure Elem=Element datatype Tree = Node of int*Elem.T*Tree list type Heap = Tree list fun rank (Node(r,_,_))=r fun link (t1 as Node (r,x1,c1), t2 as Node (_,x2,c2))= if Elem.leq(x1,x2) then Node (r+1,x1,t2::c1) else Node (r+1,x2,t1::c2) fun insTree (t,[])=[t] |insTree (t,ts as t'::ts')= if rank t < rank t' then t::ts else insTree(link(t,t'),ts') fun insert (x,ts)=insTree(Node(0,x,[]),ts) fun merge(ts1,[])=ts1 |merge([],ts2)=ts2 |merge (ts1 as t1::ts1', ts2 as t2::ts2')= if rank t1 < rank t2 then t1::merge(ts1',ts2) else if rank t2 < rank t1 then t2::merge(ts1,ts2') else mwc(link(t1,t2),ts1',ts2') (*mwc=merge with carry*) and mwc (c,ts1,[])=insTree(c,ts1) |mwc (c,[],ts2)=insTree(c,ts2) |mwc (c,ts1 as t1::ts1', ts2 as t2::ts2')= if rank c < rank t1 then if rank c < rank t2 then c::merge(ts1,ts2) else mwc(link(c,t2),ts1,ts2') else mwc(link(c,t1),ts1',ts2) end 函子BinomialHeap(元素:有序):HEAP= 结构 结构元素=元素 数据类型树=int*Elem.T*树列表的节点 类型Heap=树列表 乐趣等级(节点(r,,))=r 有趣的链接(t1作为节点(r,x1,c1),t2作为节点(x2,c2))= 如果元素leq(x1,x2) 然后节点(r+1,x1,t2::c1) else节点(r+1,x2,t1::c2) 趣味树(t,[])=[t] |insTree(t,ts作为t’::ts’)= 如果秩t<秩t',则t::ts else insTree(link(t,t'),ts')) 有趣的插入(x,ts)=insTree(节点(0,x,[]),ts) 有趣的合并(ts1,[])=ts1 |合并([],ts2)=ts2 |合并(ts1作为t1::ts1',ts2作为t2::ts2')= 如果秩t1<秩t2,则t1::merge(ts1',ts2) 否则,如果秩t2<秩t1,则t2::merge(ts1,ts2') else mwc(链路(t1、t2)、ts1’、ts2’) (*mwc=与进位合并*) mwc(c,ts1,[])=insTree(c,ts1) |mwc(c,[],ts2)=insTree(c,ts2) |mwc(c,ts1为t1::ts1',ts2为t2::ts2')= 如果秩c<秩t1 然后,如果秩c<秩t2,则c::merge(ts1,ts2) else mwc(链路(c,t2),ts1,ts2’) else mwc(链路(c,t1),ts1',ts2) 结束
证明Okasaki的实现是O(logn):如果一个进位“昂贵”(需要一个或多个链接),那么它产生一个零。因此,下一次昂贵的携带将在到达该点时停止。因此,传播所有进位所需的链接总数不超过传播前二进制表示的总长度,该长度以ceil(log n)为界,其中n是较大堆的大小。

我认为这两种方式都没有多大区别。mwc是insTree和merge的手动融合组合。在没有运行任何基准测试的情况下,它可能会稍微快一点(在实际时间内,而不是在big-O中),但如果它的速度足够快,足以对应用程序产生任何影响,我会感到惊讶。如果这种直觉是正确的,那么它可以归结为个人偏好——(稍微)简单的代码与(稍微)简单的分析。谢谢,@chrisokaski。实际上,我并没有想到mwc是将insTree和merge结合起来的。更确切地说,我是在翻译二进制加法的常用手工算法,一点一点地添加,然后一路进行处理——这对我来说是思考这个问题最明显的方式。我最初想让一个helper函数为进位选择一个节点选项,但后来意识到最好将它一分为二(当然我指的是一个树选项)@chrisokaski你应该把你的评论作为答案发布!很高兴看到你在网上回答问题,顺便说一句。