【问题标题】:Prolog: Sum the values ​of the leaves in the binary treeProlog:对二叉树中叶子的值求和
【发布时间】:2021-01-31 17:32:57
【问题描述】:

我有一个二叉树,在给定一个类似的谓词的情况下,我必须在这个二叉树中添加叶子中包含的值:

sum_leafs ([Node, Left, Right], Sum).

我不明白我该怎么做,必须计算叶子的值我应该计算节点的总和,以防左右都是 nil ..

[Node, nil, nil]

所以我有类似的东西:

sum_leafs([Node, Left, Right], Sum):-
    sum_leafs(Left, Sum_Left),
    sum_leafs(Right, Sum_Right),
    Sum is (Sum_Left + Sum_Right).

我的尝试:

sum_leafs(nil, 0).
sum_leafs([Node, nil, nil], Node).
sum_leafs([_, Left, Right], Sum):-
    sum_leafs(Left, Sum_Left),
    sum_leafs(Right, Sum_Right),
    Sum is (Sum_Left + Sum_Right).

我怎样才能做到这一点?

谢谢

【问题讨论】:

    标签: prolog


    【解决方案1】:

    列表是二叉树的不良表示。让我们开始改变它:

    • 空树:nil
    • 非空树:t(Left,Value,Right)

    使用这种表示,对叶子的值求和的谓词可以写成:

    sum_leaves(Tree, Sum) :-
        sum_leaves(Tree, 0, Sum).
    
    sum_leaves(nil, Sum, Sum).
    sum_leaves(t(nil,Value,nil), Sum0, Sum) :-
        !,
        Sum is Sum0 + Value. 
    sum_leaves(t(Left,_,Right), Sum0, Sum) :-
        sum_leaves(Left, Sum0, Sum1),
        sum_leaves(Right, Sum1, Sum).
    

    但是第一个版本不是尾递归的。我们可以改进它:

    sum_leaves(Tree, Sum) :-
        sum_leaves(Tree, 0, Sum).
    
    sum_leaves(nil, Sum, Sum).
    sum_leaves(t(nil,Value,nil), Sum0, Sum) :-
        !,
        Sum is Sum0 + Value. 
    sum_leaves(t(Left,_,Right), Sum0, Sum) :-
        sum_leaves(Left, LeftSum),
        Sum1 is Sum0 + LeftSum,
        sum_leaves(Right, Sum1, Sum).
    

    【讨论】:

    • 坚持住保罗。因此,两个版本都将值累加到Sum0 以提供Sum。第一个版本不是尾递归的,因为我们在左右孩子上有双重递归下降(但编译器可能仍然可以设法在沿右孩子的递归下降上构建一个循环)。但是第二个版本真的比第一个“更多”尾递归吗?毕竟,sum_leaves(Left, Sum0, Sum1) 的调用只是隐藏在对sum_leaves(Left, LeftSum) 的中间调用之后,它最终以sum_leaves(Left, 0, LeftSum) 结束,没有太大变化?
    • 在SWI-Prolog中,我们可以使用sum_leaves/3的最后一个子句中的谓词prolog_current_frame/1来检查TCO:sum_leaves(t(Left,_,Right), Sum0, Sum) :- prolog_current_frame(Frame), writeln(Frame), ...。通过此修改,查询 ?- sum_leaves(t(nil,4,t(nil,3,t(nil,2,t(nil,1,nil)))),S). 产生 3 倍相同的帧号(= 堆栈不增长),但查询 ?- sum_leaves(t(t(t(t(nil,1,nil),2,nil),3,nil),4,nil),S). 产生 3 个不同的帧号(= 堆栈确实增长)。
    【解决方案2】:

    我是这样做的:

    sum_leafs(nil, 0).
    sum_leafs([Node, nil, nil], Node).
    sum_leafs([_, Left, Right], Sum):-
        sum_leafs(Left, Sum_Left),
        sum_leafs(Right, Sum_Right),
        Sum is (Sum_Left + Sum_Right).
    

    【讨论】:

    • 两件事:命名有点混乱,因为“节点”并不是真正的节点,它是节点的“值”。更重要的是:此代码有一些不需要的“替代解决方案”。如果您点击一个叶子(一个具有两个nil 子术语的节点),则第二个子句适用,但第三个子句适用。第二个解决方案总是给出 0 而不是预期的“叶子总和”。为避免这种情况,如果匹配,则通过在正文中放置一个切口来提交第二个子句:sum_leafs([Value, nil, nil], Value) :- !. 或在第三个子句中添加一个保护:(Left\==nil;Right\==nil)
    猜你喜欢
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多