【问题标题】:How to implement a binary heap using list in OCaml?如何在 OCaml 中使用列表实现二进制堆?
【发布时间】:2013-02-22 00:23:21
【问题描述】:

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

感觉list使用起来很吃力,苦苦挣扎了2天,不得不来这里寻求建议和提示。


这是我目前的想法

显然,我不能使用原始的array based 算法来使用列表来实现它。

我想使用的是binary tree。我保留了invariant,即节点应该大于任何级别低于其的节点。

我大致弄清楚了如何实现insert,虽然我不确定它是否正确。

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

当插入x 时,我与一个节点(从根,递归)进行比较。假设x < the value of the node,那么

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

如果节点的孩子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。我看过,但我觉得它的方式与我的完全不同,我无法理解。谁能帮我理解一下?

【问题讨论】:

  • 为什么不直接写x &gt; v 而不是compare x v &gt; 0
  • @AndrejBauer 是的,你是对的。我刚开始的想法是我应该有一个compare 函数作为堆、排序等的参数,因为某些用户可能不使用Pervasives.compare
  • 我明白了。在这种情况下,您可能会对使用仿函数感兴趣。顺便问一下,你看过标准库中 OCamlMap 模块的源代码吗?它有一个平衡二叉搜索树的实现,就像你正在实现的那样。
  • @AndrejBauer 谢谢。实际上我还没有学过仿函数,但是会学的:D
  • 你为什么说你在尝试使用列表,你的代码中没有列表?

标签: functional-programming ocaml


【解决方案1】:

这个插入代码对我来说看起来很不错。 (我有一阵子被计数弄糊涂了,但现在我看到他们在计算后代的数量。)

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

如果您查看冈崎(如果您遇到困难,您可以这样做!),您会发现他的树有一个额外的不变量,可以更轻松地执行这些操作。我很确定这不是我马上想出来的。他的实现基于合并两棵树的操作。它用于插入和删除。

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

更新

Okasaki 的书Purely Functional Data Structures 是他博士论文的详细阐述。似乎优先级队列只出现在书中——对不起。如果你真的对 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 在现实生活中是什么样的。他在西点军校任教,在那里找到他的个人页面并不难。它可能会满足您的一些好奇心。

【讨论】:

  • 谢谢。 Okasaki 是什么意思?你的意思是cs.cmu.edu/~rwh/theses/okasaki.pdf?还是书amazon.co.uk/Purely-Functional-Structures-Chris-Okasaki/dp/…
  • 另外,尽管冈崎,您同意我对insert 的想法或实施吗?有什么改进的建议吗?
  • (就像我说的,插入看起来不错。这完全取决于您是否可以在树上实现delete_min。请参阅更新。)
  • 你认为你的合并是 logN 吗?还有树的平衡如何?
  • 好吧,我试过了。就像我说的我没有时间仔细检查,我想你可能想要这样做。我试图保持平衡,您可能想再次验证这一点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-24
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 2013-11-30
相关资源
最近更新 更多