【问题标题】:Creating Binary Tree in Prolog在 Prolog 中创建二叉树
【发布时间】:2021-12-20 21:04:12
【问题描述】:

我是 Prolog 的初学者,我的第一个任务是实现一个函数construct(),它从一个列表构建一个二叉树。我知道我的代码有问题或遗漏了一些东西,但我不能指望它。我也认为辅助方法可能是必要的,但我想不出如何去做。 到目前为止,这是我的代码:

construct([],nil).
construct(E, tree(E,nil,nil)).
construct([H|T], tree(H, construct(T,R), nil)):- 
          T>H.
construct([H|T], tree(H, construct(T,R), nil)):- 
          T<H.

【问题讨论】:

  • 您需要创建二叉树还是二叉搜索树
  • 您没有在这里回答的一个最重要的问题是您期望的树的种类。查看this reddit thread,确保您阅读了问题和所有 cmets(其中只有 3 个,所以我希望不要做太多工作)。也许这会让你更接近提出一个好问题。
  • PS:如果你只是从我上面链接的 reddit 中获取代码并写道:sort(List, Sorted), list_to_tree(Sorted, Tree) 那么你将在Tree 中拥有一个平衡的二叉搜索树,其中包含所有 unique List 中的元素。您需要使它们唯一,否则它将不是二叉搜索树。但这已经根据您的问题中的信息不足假设了很多事情。

标签: prolog binary-tree


【解决方案1】:

在 Prolog 中,我们可以使用:

  • 原子nil代表一棵二叉树,并且
  • t(Left, Root, Right) 形式的术语表示一个非空二叉树,其中Root 是树根的值,LeftRight 是还有表示二叉树的术语。

为了使二叉树更易于查看,您可以使用以下谓词:

show(T) :-
    show(T, 0).

show(nil, _).
show(t(Left, Root, Right), Indent) :-
    Indent1 is Indent + 3,
    show(Right, Indent1),
    format('~*c~w\n', [Indent, 32, Root]),
    show(Left, Indent1).

例子:

?- show( t(t(nil,1,nil), 2, t(nil,3,nil)) ).
   3
2
   1
true.

List 是一个列表,表示任意二叉树的in-order 遍历,如下图所示:

那么,由于不同的二叉树可以有相同的中序遍历,那么对应于这样一个列表的二叉树可以描述如下:

% convert from in-order traversal list to binary tree

list_to_bt(List, Tree) :-
    (   List = []
    ->  Tree = nil
    ;   Tree = t(Left, Root, Right),
        append(Prefix, [Root|Suffix], List),
        list_to_bt(Prefix, Left),
        list_to_bt(Suffix, Right) ).

例子:

?- list_to_bt([1,2,3], T), show(T).
      3
   2
1
T = t(nil, 1, t(nil, 2, t(nil, 3, nil))) ;
   3
      2
1
T = t(nil, 1, t(t(nil, 2, nil), 3, nil)) ;
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil)) 
...
false.

如果您只想获得平衡二叉树(例如,对于每个节点,左右子树大小的绝对差最大为 1),那么您可以包括这个约束如下:

% convert from in-order traversal list to balanced binary tree (bbt)

list_to_bbt(List, Tree) :-
    (   List = []
    ->  Tree = nil
    ;   Tree = t(Left, Root, Right),
        append(Prefix, [Root|Suffix], List),
        length(Prefix, Size1),
        length(Suffix, Size2),
        abs(Size1 - Size2) =< 1,
        list_to_bbt(Prefix, Left),
        list_to_bbt(Suffix, Right) ).

例子:

?- list_to_bbt([1,2,3], T), show(T).
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, nil)) ;
false.

如果您只想要任意列表中的平衡二叉搜索树,则必须在创建平衡二叉树之前对该列表进行排序:

% convert from arbitrary list to balanced binary search tree (bbst)

list_to_bbst(List, Tree) :-
    sort(List, Sorted),
    list_to_bbt(Sorted, Tree).

例子:

?- list_to_bbst([3,1,7,5,4,2,6], T), show(T).
      7
   6
      5
4
      3
   2
      1
T = t(t(t(nil, 1, nil), 2, t(nil, 3, nil)), 4, t(t(nil, 5, nil), 6, t(nil, 7, nil))) ;
false.

?- list_to_bbst([3,1,4,2], T), show(T).
      4
   3
2
   1
T = t(t(nil, 1, nil), 2, t(nil, 3, t(nil, 4, nil))) ;
   4
      3
2
   1
T = t(t(nil, 1, nil), 2, t(t(nil, 3, nil), 4, nil)) ;
   4
3
      2
   1
T = t(t(nil, 1, t(nil, 2, nil)), 3, t(nil, 4, nil)) ;
   4
3
   2
      1
T = t(t(t(nil, 1, nil), 2, nil), 3, t(nil, 4, nil)) ;
false.

【讨论】:

    【解决方案2】:

    首先:Prolog 中没有函数,只有谓词。所以,如果你写tree(H, construct(T,R), nil)(来自你的第二个子句),那么你已经创建了一个看起来像这样的结构(或“术语”):

                tree
             /   |     \
           /     |       \
           H  construct  nil
               /   \
              /     \
             T       R
    

    这不是你想要的。

    相反,你应该写这样的东西(尽管这也不会做你想要的——见下文):

    construct([H|T], tree(H, Tree, nil)):- 
        T>H,
        construct(T, R, Tree).
    

    第二:您应该有一种简单的方法来区分值和非值。您已选择 nil 用于非值,因此您可以使用 value(V) 之类的东西来存储值。

    现在,想想node 的样子。它有 2 个部分(左侧和右侧),每个部分可以是 nilvaluenode。例如,您希望 [1,2,3] 生成一棵看起来像

    的树
    node(node(value(1),
              value(2)),
         value(3))
    

    至于辅助谓词(不是“函数”),一个明显的是将单个值插入树的谓词。因此,只需列出所有可能性:

    insert_value(X, node(nil, nil), node(value(X), nil)).  % insert into an empty node
    insert_value(X, node(value(Y), nil), node(value(X), value(Y))) :- X < Y.
    insert_value(X, node(value(Y), nil), node(value(Y), value(X))) :- X > Y.
    insert_value(X, node(node(A,B), value(Y)), node(T1, value(Y))) :-
        X < Y,
        insert_value(X, node(A,B), T1).
    

    等等

    如果您这样做,您会发现这不是树的最佳表示,但我会留给您提出更好的表示。 (顺便说一句,所有的node 函子都不需要有 2 个元素;可以有 node - 不包含任何内容;node(X) - 包含单个项目;node(X,Y) - 包含 2 个项目)。

    此外,您还需要决定在插入树中已经存在的值时要做什么 -- insert_value/3 是否失败、它什么也不做,还是它允许多个相同的值?

    一旦你完成了这项工作,你就可以编写一个谓词来插入一个值列表:

    insert_values([], Tree, Tree).
    insert_values([X|Xs], Tree0, Tree) :-
        insert_value(X, Tree0, Tree1),
        insert_values(Xs, Tree1, Tree).
    

    这是一种很常见的模式;它的一般形式称为“foldl”。

    最后,用一个空节点的初始值来完成整个事情:

    
    insert_values(Values, Tree) :-
        insert_values(Values, node(nil,nil), Tree).
    

    在 Prolog 中定义具有不同数量参数的谓词也很常见;在这种情况下,insert_values/2 使用具有空节点 (node(nil,nil)) 初始值的 insert_values/3

    【讨论】:

    • 这是一个风格问题,但是创建具有相同名称和不同数量参数的谓词(甚至复合术语)可能会导致错误。我建议这样做的唯一地方是在库的“公共接口”中,为参数提供默认值。在这种特殊情况下,您还可以为创建 node/1、node/2、node/3 复合术语提出论据,但我对此有点矛盾。
    • 我经常使用具有不同数量参数的谓词(或具有不同数量的复合术语),这对我来说并不是一个重要的错误来源。但是,最好不要过度使用它——它真的应该只在存在“可选”参数但语义基本相同的情况下使用。正如@TA_intern 所说,这在某种程度上是品味和经验的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多