如何使用OCaml中的列表实现二进制堆?

时间:2013-03-06 23:45:51

标签: functional-programming ocaml

我正在OCaml中实现一个binary heap使用列表,只是为了提高我的OCaml技能。

我觉得使用列表非常困难,经过2天的努力,我必须到这里寻求建议和提示。


到目前为止,这是我的想法

显然,我不能使用orignal array based算法来使用list来实现它。

我想要利用的是binary tree。我保持invariant节点应该比任何级别低于它的节点大。

我粗略地想出了如何实现insert,虽然我不确定它是否正确。

对于二叉树,每个节点都有two childrenvalue size n,这是offsprings的总数。此n用于平衡树。

插入x时,我会与节点(来自root,递归)进行比较。假设x < the value of the node,然后

如果节点的一个或两个孩子都是Leaf,那么我将x插入到该Leaf位置。

如果节点的子节点的none为Leaf,那么我将选择n小于recursively insert的子节点。


这是我的代码

type 'a heap = 
  | Node of 'a * 'a heap * 'a heap * int
  | Leaf 

exception EmptyHeapException

let create_heap () = Leaf;;

let rec insert x = function
  | Leaf -> Node (x, Leaf, Leaf, 0)
  | Node (v, l, r, n) ->
    let (stay, move) = if x > v then (x, v) else (v, x)
    in 
    match (l, r) with 
      | (Leaf, Leaf) -> 
        Node (stay, Node (move, Leaf, Leaf, 0), Leaf, 1)
      | (Leaf, _) -> 
        Node (stay, Node (move, Leaf, Leaf, 0), r, n+1)
      | (_, Leaf) ->
        Node (stay, l, Node (move, Leaf, Leaf, 0), n+1)
      | (Node (_, _, _, n1), Node (_, _, _, n2)) ->
        if n1 <= n2 then
          Node (stay, (insert move l), r, n1+1)
        else 
          Node (stay, l, (insert move r), n2+1);;

好的,我有以下问题。

  1. 我正朝着正确的方向前进吗?我的想法或实施是否正确?
  2. 我陷入了实施get_top功能的困境。我不知道如何继续。任何提示?
  3. ocaml电池实现了高效batHeap.ml。我看了一眼,但我觉得它的方式与我完全不同,我无法理解。任何人都可以帮我理解吗?

1 个答案:

答案 0 :(得分:3)

这个插入代码对我来说非常好看。 (我对这些计数感到困惑了一段时间,但现在我看到他们正在计算后代的数量。)

删除最大元素(根)的功能基本上是删除,这总是最难的。实质上,您需要合并两棵树,同时保持您的不变性。我现在没有时间详细研究它,但我认为这将成为可能。

如果你看看冈崎(如果你被卡住就可以做到!)你会看到他的树有一个额外的不变量,这使得更容易做这些操作。我很确定这不是我马上想出来的。他的实现基于合并两棵树的操作。它用于插入和删除。

快速浏览一下电池堆代码是基于“二叉树”,实际上要复杂得多。他们在冈崎也有解释。

<强>更新

Okasaki的书纯功能数据结构是他博士论文的详细阐述。似乎优先级队列只出现在书中 - 抱歉。如果你真的对FP很感兴趣并且没有太多现金,这本书真的值得拥有。

正如我所说,你的插入代码看起来很棒。在我看来,你实际上有两个不变量:

  • 节点中的值小于或等于其子树根的值(排序不变量)。

  • 节点的子树种群最多相差1(余额不变)。

正如我所说的,我没有时间详细验证,但它看起来像你的插入代码维护不变量,因此是O( log n )。

此结构的有用性取决于您是否能够在保留这两个不变量的同时删除O( log n )中的根。

删除草图将是这样的:

let pop = function Leaf -> 0 | Node (_, _, _, p) -> p

let rec merge a b =
  (* populations of a, b differ by at most one. pop a >= pop b *)
  match a, b with
  | Leaf, Leaf -> Leaf
  | Leaf, _ -> b
  | _, Leaf -> a
  | Node (av, al, ar, ap), Node (bv, bl, br, bp) ->
      if av >= bv then Node (av, merge al ar, b, ap + bp)
      else Node (bv, merge al ar, insert av (delete_min b), ap + bp)

and delete_min = function
  | Leaf -> Leaf
  | Node (_, Leaf, Leaf, _) -> Leaf
  | Node (_, l, Leaf, _) -> l
  | Node (_, Leaf, r, _) -> r
  | Node (_, l, r, _) ->
    if pop l >= pop r then merge l r else merge r l

我仍然没有太多时间,因此可能需要对正确性或复杂性进行修复。

<强>更新

作为一个纯粹的大脑家伙,我(真的)从未想过Chris Okasaki在现实生活中的样子。他在西点军校教书,在那里找到他的个人页面并不太难。它可能会满足你的一些好奇心。