【问题标题】:PreOrder Tree Traversal in PrologProlog中的PreOrder树遍历
【发布时间】:2023-11-10 16:32:01
【问题描述】:

我有这个 Prolog 谓词用于 PreOrder 遍历树:

preOrder(nil, []).
preOrder(node(X, nil, nil), [X]).
preOrder(node(X, L, _), [X|T]) :- preOrder(L, T).
preOrder(node(X, _, R), [X|T]) :- preOrder(R, T).

问题是,它返回一个不完整的列表。例如,我得到:

?- preOrder(node(1, node(2, node(3,nil,nil), node(4,nil,nil)), node(5,nil,nil)), L). 
L = [1,2,3]

什么时候应该是L=[1,2,3,4,5]

为什么会突然停止?

【问题讨论】:

    标签: prolog binary-tree tree-traversal preorder


    【解决方案1】:

    查看 Prolog 产生的答案。这不是一个:

    | ?- preOrder(node(1,node(2,node(3,nil,nil),node(4,nil,nil)),node(5,nil,nil)),L).
    L = [1,2,3] ? ;
    L = [1,2,3] ? ;
    L = [1,2,3] ? ;
    L = [1,2,4] ? ;
    L = [1,2,4] ? ;
    L = [1,2,4] ? ;
    L = [1,5] ? ;
    L = [1,5] ? ;
    L = [1,5] ? ;
    no
    

    您的每条规则都独立于其他规则描述某些部分。但是您需要将它们全部描述在一起。

    解决这个问题的最佳方法是to use DCGs

    【讨论】:

      【解决方案2】:

      因为你有两个递归子句,每个子句都只到树的一侧,所以它停止了。 尽管不需要第二个,但您也有两个基本案例。

      因此,您将删除第二个子句并仅将两个递归子句连接到一个子句中,该子句附加两个分支的结果。

      例如:

      preOrder(nil, []).
      preOrder(node(X, L, R), [X|T]) :- 
        preOrder(L, LT), 
        append(LT, RT, T), 
        preOrder(R, RT).
      

      您也可以使用累加器进行遍历:

      preOrder(Tree, List):-
        preOrder(Tree, [], RList),
        reverse(RList, List).
      
      preOrder(nil, List, List).
      preOrder(node(X, L, R), List, NList) :- 
          preOrder(L, [X|List], MList), 
          preOrder(R, MList, NList).
      

      请注意,正如一位评论者所说,preOrder 的这些定义在给定遍历的情况下无法正常生成树。

      您可能希望使用 DCG 来定义一个可逆的过程,在内部使用开放列表:

      preOrder(nil)-->[].
      preOrder(node(X, L, R))-->[X], preOrder(L), preOrder(R).
      

      你可以使用phrase/2来调用它:

      ?- phrase(preOrder(node(1, node(2, node(3,nil,nil), node(4,nil,nil)), node(5,nil,nil))), L).
      L = [1, 2, 3, 4, 5].
      

      【讨论】:

      • 第一个版本:对于所有非空列表 LT 未实例化,preOrder(T, L) 不会终止。第二个版本:循环也适用于L = []
      • 是的,但是@OP 想要遍历给定的树,而不是从列表遍历中生成树。
      最近更新 更多