我如何检查是完整的二叉树?

时间:2017-03-17 20:58:34

标签: prolog binary-tree

如何在prolog中检查完整的二叉树?我有2个基本案例

1)如果是空树,则返回是

2)如果只是root,则返回yes

我被困在第三个,我不知道该怎么办。我们只允许使用1 arity:full(T)。

btree([]).
btree([H|T]):-btree(T).
btree([H|T]):-btree(H),btree(T).

full([]).
full([H]).
full([H|T]):-        
任何人都可以指导我。我的想法是一棵树没有两棵非空树,那么它就是一棵完整的二叉树。

P / S:我仍然是stackoverflow的新手。如果我确实问了一些愚蠢或不正确的方式,请告诉我。我想学习如何使用stackoverflow并以正确的方式确保它。

3 个答案:

答案 0 :(得分:3)

我首先可能会为二叉树选择不同的表示形式。在Prolog中,使用简单原子(例如nil)作为nil节点,使用btree(Value, Left, Right)作为树术语,通常更常规和有效。除此之外,该解决方案看起来非常像@pyon建议的那样。

% full_btree succeeds if `Tree` is a full binary tree

full_btree(Tree) :-
    full_btree(Tree, _).

full_btree(nil, 0).
full_btree(b(_, LeftTree, RightTree), Depth) :-
    Depth #> 0,
    SubDepth #= Depth - 1,
    full_btree(LeftTree, SubDepth),
    full_btree(RightTree, SubDepth).

条件Depth #> 0确保无论输入如何,深度都不会变为负值,从而有助于确保终止。

答案 1 :(得分:2)

我将假设你代表树木如下:

  • 叶子是一个空列表。
  • 非叶节点是一个三元素列表,其最后两个元素是子树。

然后:

helper([], 0).
helper([_,L,R], H) :- H #= G + 1, helper(L, G), helper(R, G).
/* Old version: helper([_,L,R], H) :- helper(L, G), helper(R, G), H is G + 1.
 * The improvement in the new version was suggested by lurker. Thanks!
 */

full(T) :- helper(T, _).

这是有效的,因为完整的二叉树可以按如下方式归纳定义:

  • 叶节点是高度为0的完整二叉树。
  • 一个非叶节点,其子节点都是高度为G的完整二叉树,它本身就是一个高度为G + 1的完整二叉树。

答案 2 :(得分:2)

好的,既然已经有一个答案没有以面值回答这个问题,那么这就是我的。它没有为the answer by @lurker添加任何东西,它只是提供了太多的评论细节和解释。它也完全避免了CLP(FD),这就是为什么我认为它应该是一个单独的答案。

您可以使用更传统的二叉树表示来启动(如@lurker所示)。空树为nil,非空树为bt(Value, Left, Right),其中Value为此节点的值,LeftRight为左侧,正确的子树。这是传统的表示,因为它至少具有更高的内存效率。一个“叶子”(没有子树的树)是你的原始代表:

.(Value, .([], .([], [])))

而不是:

bt(Value, nil, nil)

表示两者所需的内存量在不同的Prolog实现之间会有所不同,但我不知道如何使第一个小于而不是第二个。

然后:作为@false commented above,列表通常是物品的集合,通常具有以下属性:

  • 集合中没有任何东西或任何数量的东西;
  • 事情的顺序很重要;
  • 所有的事情都是一样的。

使用像你这样的列表会打破最后一个约定:第一个参数是一个值,而第二个和第三个参数是树。

这并不排除使用列表来表示二叉树,但它不熟悉。

有了这个方法:后继算术是一种实际算术的愚蠢方式,但如果你想对非负整数使用模式匹配,这是非常方便的。你不能使用内置的整数类型的Prolog,比如0-23或其他什么。后继算术为您提供:

  • 从结构上不同于所有正整数的零:0 vs s(_)
  • 通过模式匹配完成加法和减法,两者都使用相同的操作:X + 1s(X)
  • 负数不可能

所以,你可以像这样定义你的“完整树”:

full_btree(T) :-
    full_btree(T, _).

full_btree(nil, 0).
full_btree(bt(_, L, R), s(D)) :-
    full_btree(L, D),
    full_btree(R, D).

s(D)和两个D表示第一个参数中的树比子树更深,并且两个子树的深度相同。空树nil的深度为0(如full_btree/2的第一个子句中所定义)。

其工作原理如下:

?- full_btree(nil).
true.

?- full_btree(bt(x, nil, nil)).
true.

?- full_btree(bt(x, bt(y, nil, nil), nil)).
false.

?- full_btree(bt(x, bt(y, nil, nil), bt(z, nil, nil))).
true.

?- full_btree(T), numbervars(T).
T = nil ;
T = bt(A, nil, nil) ;
T = bt(A, bt(B, nil, nil), bt(C, nil, nil)) ;
T = bt(A, bt(B, bt(C, nil, nil), bt(D, nil, nil)), bt(E, bt(F, nil, nil), bt(G, nil, nil))) . % and so on

还有一件事:要关闭圆圈,你也可以使用列表进行后继算术。只需使用[]代替0 [_|X]代替s(X)。有了这个,你会得到:

full_tree(nil, []).
full_tree(bt(_, L, R), [_|D]) :-
    full_tree(L, D),
    full_tree(R, D).

内存效率略低,而不是s(s(s(0))) .(_, .(_, ,(_, [])))。然而!现在使用后继符号整数制作实际整数更容易,反之亦然:只使用length/2。在纯Prolog中编写一个在s(s(...))和整数之间转换的谓词的谓词并不是那么简单。我认为可以在Stackoverflow中搜索这些问题。