如何使用Prolog中的此AVL谓词从元素列表中创建平衡树?

时间:2013-04-29 17:09:55

标签: tree prolog avl-tree

我正在研究Prolog,我发现很难实现一个带有列表并从中构建平衡树的谓词。

我已经实现了构建 AVL树的这些谓词(我从Bratko书中获取它并且工作正常):

%%%  A program for constructing and searching an avl tree.

%%%  Based on Bratko pp 244ff. 

%%%  Build the tree. 

%% The root of the tree is Key.

addavl( nil/0, Key, avl(nil/0, Key, nil/0)/1 ).    

addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
    eq(Y, Key),
    !,
    NewTree = avl(Left, Y, Right)/Hy.

addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
    gt(Y, Key),
    addavl(Left, Key, avl(Left1, Z, Left2)/_ ),
    combine(Left1, Z, Left2, Y, Right, NewTree).  

addavl( avl(Left, Y, Right)/Hy, Key, NewTree):-
    gt(Key, Y),
    addavl(Right, Key, avl(Right1, Z, Right2)/_ ),
    combine(Left, Y, Right1, Z, Right2, NewTree).  

combine(T1/H1, A, avl(T21, B, T22)/H2 , C, T3/H3,
        avl(avl(T1/H1, A, T21)/Ha, B, avl(T22, C, T3/H3)/Hc)/Hb ):-
    H2 > H1,
    H2 > H3,
    Ha is H1 + 1,
    Hc is H3 + 1,
    Hb is Ha + 1.

combine(T1/H1, A, T2/H2, C, T3/H3,
        avl(T1/H1, A, avl(T2/H2, C, T3/H3)/Hc)/Ha ):-
    H1 >= H2,
    H1 >= H3,
    max1(H2, H3, Hc),
    max1(H1, Hc, Ha).

combine(T1/H1, A, T2/H2, C, T3/H3,
        avl(avl(T1/H1, A, T2/H2)/Ha, C, T3/H3)/Hc ):-
    H3 >= H2,
    H3 >= H1,
    max1(H1, H2, Ha),
    max1(Ha, H3, Hc).

max1(U, V, Max):-
    (  U > V,
       !,
       Max is U + 1
    ;  Max is V + 1
    ). 

eq(X, Y):-
    X == Y,
    !,
    write(X),
    write(' Item already in tree\n'). 

所以我有 addavl(Tree, Element, NewTree/Height) 谓词,它将新元素添加到生成新AVL树的Tree

现在我想创建一个新的谓词,使用这个 addavl/3 谓词从元素列表中创建一个新的AVL树。

例如,如果我有列表:[1,2,3],则此新谓词将创建一个包含元素 1,2,3 的新AVL树。

我正在努力做到这一点,但我发现有些困难。

我已经实现了类似的东西(但它不起作用):

%% BASE CASE: The list is empty, so there is no element to insert 
%%            into the AVL Tree: 

buildTreeList(Tree, [], NewTree, Height) :- !.


%% If I am inserting the first element in the AVL Tree 
%% the hight H of the Tree after the insertion is 1: 

buildTreeList(Tree, [Head|Tail], NewTree, 1) :-  
    addavl(nil/0, Head, avl(nil/0, Head, nil/0)/H),
    Tree = nil/0,
    NewTree = avl(nil/0, Head, nil/0)/1,
    buildTreeList(NewTree, Tail, NT, Height).

buildTreeList(Tree, [Head|Tail], NewTree, H) :- 
    addavl(Tree, Head, avl(Tree, Head, NewTree)/H),
    buildTreeList(NewTree, Tail, NT, Height). 

我的想法是: addavl/3 谓词为新的AVL树添加了一个元素,并为我提供了这棵新树的高度(因为我有这对夫妇 NewTree / Height )。

所以我的想法是:

  1. 滚动项目列表,直到空列表(基本情况:列表中没有项目,因此我不会在AVL树中插入任何内容)

  2. 将列表的任何元素插入AVL树。

  3. 如果AVL树为空它有height=0所以我创建了一个新的AVL树:

     addavl(nil/0, Head, avl(nil/0, Head, nil/0)/H)
    
  4. 如果AVL树不为空,我会插入其中。

  5. 但它不起作用,可能是做这件事的错误方法。

    有人可以帮助我吗?

1 个答案:

答案 0 :(得分:2)

您正在尝试重新实施maplist/N

你有addavl(Tree, Element, NewTree)。树已经采用T/Height的形式。您必须以nil/0开头。

buildTree(List,Tree):-
  length(List, N), 
  length(L1, N), append(L1, [Tree], [nil/0 | L2]),
  maplist( addavl, L1, List, L2).

(未经测试)。

关键是,使用addavl/3作为给定的不透明谓词,不要重新审视它的定义。

两个列表L1L2之间的逻辑变量共享(移位一个位置)用于安排累计结果从计算的一个步骤传递到下一个,表示nil/0,直到在最后一步中构建完整的树Tree。为方便起见,这是交易效率。您应该以直接递归样式重新实现它,特别是如果预期元素列表很长。


注释:是否使用maplist或手动滚动直接递归解决方案,是语法问题。两种变体描述了相同的迭代计算过程,通过调用addavl,使用先前调用的输出作为下一个的输入,逐步将元素添加到树中。一个常见的模式,例如不幸的是,在Haskell中,被一个名为的高阶程序捕获 - 惊喜! - iterate

(仅在更高级别的情况下才是这样。在具体实现中,如SWI Prolog,可以优化得比另一个好得多。使用列表,这里可能效率低于其他变量)。