检查树是否是堆[在prolog中]

时间:2009-12-03 06:47:55

标签: prolog

如果Tree是一个满足堆属性和shape属性的堆,我需要一些帮助在prolog中编写谓词堆(Tree)成功:

  • 形状属性:树是一个几乎完整的二叉树;就是所有级别 除了可能最后一个(最深)完全填充之外,如果是最后一个 树的级别不完整,该级别的节点从左到右填充。
  • 堆属性:每个节点大于或等于其子节点 根据一些对整个数据固定的比较谓词 结构

我想使用2种不同的表示来编写2个谓词:

  1. node(K,L,R)代表一棵树,其中包含键K(整数),左子树L和右子树R

  2. 整数列表,从根到叶,从左到右

  3. 例如:

    ?- heap1(node(5, node(4, empty, empty), empty)).
    true.
    

    ?- heap2([5, 4, 3, 2]).
    true
    ?- heap2([7, 6, -1, -3, 5]).
    true
    ?- heap2([]).
    true
    

    我目前对Prolog的知识非常有限,需要很多帮助来定义这些谓词。

1 个答案:

答案 0 :(得分:2)

这个问题在最初发布之后就被删除了,因为OP并没有事先询问她问homework question这个问题。由于作业的截止日期已经过去,我决定取消删除这个答案。

我不是Prolog的大师,但我提出了一些想法。我认为heap1/1heap2/1更容易实现,因为它的“树结构”更加明显。事实上,我要做的是实施heap1/1,之后我将根据 heap2/1实施heap1/1

树实现:heap1/1

empty树是binary heap。至于node(K, L, R),它是一个堆,如果:

  1. LR是堆。
  2. 如果L和/或R不是empty,则其密钥小于或等于K
  3. 如果L的深度为 d ,则R的深度为 d d - 1
  4. 所以需要做的是检查给定的二叉树是否满足这些属性。

    • 给定node,我们需要遍历它以了解它所代表的树的深度。因此,我们将根据谓词heap/1定义heap/2,其中第二个参数是树深度。最后我们不关心实际深度,因此我们定义:

      heap1(H) :- heap1(H, _).
      
    • 那么heap/2呢?基本案例是empty,这是一个深度为0(或1的堆,但这与我们的目的无关)。因此:

      heap1(empty, 0).
      
    • 对于node(K, L, R),我们必须递归。我们需要测试上面概述的堆属性并计算树深度H。因此:

      heap1(empty, 0).
      heap1(node(K, L, R), H) :-
        heap1(L, LH), heap1(R, RH),
        (L = node(LK, _, _) *-> K @>= LK; true),
        (R = node(RK, _, _) *-> K @>= RK; true),
        (LH is RH; LH is RH + 1), H is LH + 1.
      

      此代码使用SWI-Prolog的 soft cut (*->)/2。此机制用于测试子树是否为非空,如果是,则验证其密钥是否小于或等于K(使用(@>=)/2)。谓词true/0对应于其中一个子树 为空的情况。 (is)/2用于比较子树的深度并计算整棵树的深度。

    列表实现:heap2/1

    我假设heap2/1应该检查它的唯一参数是否是表示存储为数组(或Ahnentafel list)的堆的列表。如果是,那么,假设零索引列表,索引 i 的节点的子节点在索引 2i + 1 2i + 2

    • 因此,父节点不一定存储在其子节点旁边。更具体地说,在列表中的 i 位置,我们需要跳过 i i + 1 位置以到达子树的键。我们将定义一个谓词skipn/3来帮助我们:

      skipn(N, L, E) :- (length(F, N), append(F, E, L)) *-> true; E = [].
      

      skipn(N, L, E)成功,E等于L后,N的{​​{1}}元素被剥离如果{{ 1}}是空列表,而L的长度不大于E。此实施使用length/2append/3L以及N

    • 接下来:(*->)/2的定义。我们将再次在辅助谓词的帮助下调用true/0。如果列表heap2/1(其头部位于可能更大的列表的位置heap2/3)可以转换为二叉树heap2(H, N, T),则H成功。 N将使用初始索引T调用heap2/1,并验证生成的二叉树实际上是堆。因此:

      heap2/3
    • 好吧,我们快到了。在位置0,左子树根的索引heap2(H) :- heap2(H, 0, T), heap1(T). N。同样,LI是右子树的根的索引。由于位置2 * N + 1已经RI = 2 * N + 2已跳过列表项,因此只需跳过NN元素即可达到索引LI - N和{{1} }, 分别。因此:

      RI - N