Loading docs/ds/heap/pairing-heap.md +13 −13 Original line number Diff line number Diff line Loading @@ -61,11 +61,11 @@ Node* merge(Node* a,Node* b)   到这里我们会发现,前面的几个操作都十分偷懒,几乎完全没有对数据结构进行维护,所以删除最小值是配对堆最重要的(也是最复杂)的一个操作。   考虑我们拿掉根节点之后会发生什么,根节点原来的所有儿子构成了一片森林,所以我们要把他们合并起来。   一个很自然的想法是使用merge函数把儿子们一个一个并在一起,这样做的话正确性是显然的,但是会导致复杂度退化到$ O(n)$。为了保证删除操作的均摊复杂度为$ O(\log n)$,我们需要:把儿子们两两配成一对,先用merge操作把被配成同一对的2个儿子合并到一起(见下图1),再按上述方法将新产生的堆暴力合并在一起(见下图2)。   一个很自然的想法是使用 ``merge`` 函数把儿子们一个一个并在一起,这样做的话正确性是显然的,但是会导致复杂度退化到 $O(n)$。为了保证删除操作的均摊复杂度为 $O(\log n)$,我们需要:把儿子们两两配成一对,先用 ``merge`` 操作把被配成同一对的两个儿子合并到一起(见下图1),再按上述方法将新产生的堆暴力合并在一起(见下图2)。     先实现一个辅助函数~~(实际上是主要函数)~~``merges``,作用是合并一个节点的所有兄弟。   先实现一个辅助函数``merges``,作用是合并一个节点的所有兄弟。 ##### 递归版本的 merges(推荐) Loading Loading @@ -132,7 +132,7 @@ struct Node ```   merge操作修改为:   ``merge`` 操作修改为: ```cpp Node* merge(Node* a,Node* b) Loading @@ -148,7 +148,7 @@ Node* merge(Node* a,Node* b) } ```   merges操作修改为:   ``merges`` 操作修改为: ```cpp Node* merges(Node* x) Loading @@ -162,9 +162,9 @@ Node* merges(Node* x) } ```   现在我们来考虑如何实现decrease-key操作。   首先我们发现,当我们对节点x进行dec-key操作后,以x为根的子树仍然满足配对堆性质,但x的父亲和x之间可能不再满足堆性质。   因此我们可以把整棵以x为根的子树剖出来,这样现在两棵树都符合配对堆性质了,再把他们merge起来就做完了。   现在我们来考虑如何实现 ``decrease-key`` 操作。   首先我们发现,当我们对节点x进行 ``decrease-key`` 操作后,以 $x$ 为根的子树仍然满足配对堆性质,但 $x$的父亲和 $x$ 之间可能不再满足堆性质。   因此我们可以把整棵以 $x$ 为根的子树剖出来,这样现在两棵树都符合配对堆性质了,再把他们 ``merge`` 起来就做完了。   这个操作本身复杂度显然为 $O(1)$,但会破坏原有的势能分析过程,因此均摊复杂度难以证明(目前学术界还无法给出复杂度的精确值),通常可以简单的认为复杂度为 $o(\log n)$(注意这里为小o)。 ```cpp Loading Loading
docs/ds/heap/pairing-heap.md +13 −13 Original line number Diff line number Diff line Loading @@ -61,11 +61,11 @@ Node* merge(Node* a,Node* b)   到这里我们会发现,前面的几个操作都十分偷懒,几乎完全没有对数据结构进行维护,所以删除最小值是配对堆最重要的(也是最复杂)的一个操作。   考虑我们拿掉根节点之后会发生什么,根节点原来的所有儿子构成了一片森林,所以我们要把他们合并起来。   一个很自然的想法是使用merge函数把儿子们一个一个并在一起,这样做的话正确性是显然的,但是会导致复杂度退化到$ O(n)$。为了保证删除操作的均摊复杂度为$ O(\log n)$,我们需要:把儿子们两两配成一对,先用merge操作把被配成同一对的2个儿子合并到一起(见下图1),再按上述方法将新产生的堆暴力合并在一起(见下图2)。   一个很自然的想法是使用 ``merge`` 函数把儿子们一个一个并在一起,这样做的话正确性是显然的,但是会导致复杂度退化到 $O(n)$。为了保证删除操作的均摊复杂度为 $O(\log n)$,我们需要:把儿子们两两配成一对,先用 ``merge`` 操作把被配成同一对的两个儿子合并到一起(见下图1),再按上述方法将新产生的堆暴力合并在一起(见下图2)。     先实现一个辅助函数~~(实际上是主要函数)~~``merges``,作用是合并一个节点的所有兄弟。   先实现一个辅助函数``merges``,作用是合并一个节点的所有兄弟。 ##### 递归版本的 merges(推荐) Loading Loading @@ -132,7 +132,7 @@ struct Node ```   merge操作修改为:   ``merge`` 操作修改为: ```cpp Node* merge(Node* a,Node* b) Loading @@ -148,7 +148,7 @@ Node* merge(Node* a,Node* b) } ```   merges操作修改为:   ``merges`` 操作修改为: ```cpp Node* merges(Node* x) Loading @@ -162,9 +162,9 @@ Node* merges(Node* x) } ```   现在我们来考虑如何实现decrease-key操作。   首先我们发现,当我们对节点x进行dec-key操作后,以x为根的子树仍然满足配对堆性质,但x的父亲和x之间可能不再满足堆性质。   因此我们可以把整棵以x为根的子树剖出来,这样现在两棵树都符合配对堆性质了,再把他们merge起来就做完了。   现在我们来考虑如何实现 ``decrease-key`` 操作。   首先我们发现,当我们对节点x进行 ``decrease-key`` 操作后,以 $x$ 为根的子树仍然满足配对堆性质,但 $x$的父亲和 $x$ 之间可能不再满足堆性质。   因此我们可以把整棵以 $x$ 为根的子树剖出来,这样现在两棵树都符合配对堆性质了,再把他们 ``merge`` 起来就做完了。   这个操作本身复杂度显然为 $O(1)$,但会破坏原有的势能分析过程,因此均摊复杂度难以证明(目前学术界还无法给出复杂度的精确值),通常可以简单的认为复杂度为 $o(\log n)$(注意这里为小o)。 ```cpp Loading