【问题标题】:checking if a tree is a heap [in prolog]检查树是否是堆[在序言中]
【发布时间】:2009-12-03 06:47:54
【问题描述】:

如果 Tree 是一个同时满足堆属性和形状属性的堆,我需要一些帮助在 prolog 中编写谓词堆(树):

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

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

  1. node(K,L,R) 代表一棵树,键为K(整数),左子树L,右子树R

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

例如:

?- 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 的了解非常有限,需要很多帮助来定义这些谓词。

【问题讨论】:

  • 听起来你在要求我们做你的功课?
  • 这不是家庭作业...我实际上是在自己学习 Prolog 并尝试将这些谓词作为练习来实现,但无法弄清楚。一些想法会很好
  • 啊!我相信你并输入了一个完整的答案!你的同学是对的,这是作业:www.cs.toronto.edu/~yilan/324f09/324f09a5.pdf。我看到到期日快到了;也许我会在之后发布答案。

标签: prolog


【解决方案1】:

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

我不是 Prolog 专家,但我对您的问题进行了思考。我认为heap1/1heap2/1 更容易实现,因为它的“树形结构”更加明显。事实上,我要做的是实现heap1/1,然后我将实现heap2/1heap1/1

树实现:heap1/1

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

  1. LR 是堆。
  2. 如果L 和/或R 不是empty,则它们的密钥小于或等于K
  3. 如果L 的深度为d,那么R 的深度为dd - 1

所以需要做的是检查给定的二叉树是否满足这些属性。

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

    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 的 软剪辑 (*->)/2。此机制用于测试子树是否为非空,如果是,则验证其密钥是否小于或等于K(使用(@>=)/2)。谓词true/0 对应于其中一个子树 为空的情况。 (is)/2用于比较子树的深度,计算整棵树的深度。

列表实现:heap2/1

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

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

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

    如果EL 的第一个N 元素被剥离后,E 等于L,则skipn(N, L, E) 成功如果E 是空列表而@ 的长度987654373@ 不大于N。此实现使用length/2append/3 以及再次使用(*->)/2true/0

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

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

    heap2([], _, empty).
    heap2([H|T], N, node(H, L, R)) :-
      LI is 2 * N + 1, RI is 2 * N + 2,
      LS is LI - N, RS is RI - N,
      skipn(LS, [H|T], LT), skipn(RS, [H|T], RT),
      heap2(LT, LI, L), heap2(RT, RI, R).
    

【讨论】:

    猜你喜欢
    • 2019-09-17
    • 2015-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 2020-12-19
    相关资源
    最近更新 更多