有人可以告诉我这里的树遍历是做什么的吗?

时间:2018-08-13 07:23:14

标签: algorithm ocaml

我很难理解tree_traverse函数及其功能,请有人解释。

(Peek和pop只是我所做的堆栈实现。)

type 'a tree = Leaf | Branch of ('a tree * 'a * 'a tree);;

let rec tree_traverse (t,mem,prim,seco) = match t with
  | Leaf ->
    if is_empty mem
    then []
    else tree_traverse (peek mem, pop mem, prim, seco)
  | Branch (l,nd,r) ->
    let mem1 = add (prim(l,r),mem) in
    let mem2 = add (seco(l,r), mem1) in
    nd :: tree_traverse (peek mem2, pop mem2, prim, seco)

一棵树的例子是

  let t = Branch (Branch(Branch(Leaf,1,Leaf), 2, Leaf), 3,
            Branch (Branch(Leaf,5,Leaf), 6, Branch(Leaf,7,Leaf)))

1 个答案:

答案 0 :(得分:2)

此函数实现某种工作列表算法,它以某种顺序返回节点列表,该列表取决于primseco函数以及addpop

在递归过程中,primseco参数均未更改,因此可以从参数列表中将其删除。如果我们假设以下实现方式

 let add (x,xs) = x :: xs
 let pop (x::xs) = xs
 let peek (x::xs) = x
 let prim (x,y) = x
 let seco (x,y) = y
 let is_empty = function [] -> true | _ -> false

然后tree_traverse函数将按深度优先顺序返回节点列表。

给出您的示例并将实现固定为上述指定的函数,我们现在可以跟踪该函数的执行:

tree_traverse Branch (Branch(Branch(Leaf,1,Leaf), 2, Leaf), 3,
        Branch (Branch(Leaf,5,Leaf), 6, Branch(Leaf,7,Leaf)))

Leaf的情况不匹配,因此我们转到第二种情况,并将其解构为

 | Branch (l,nd,r)) ->
    (* l is bound to Branch (Branch(Leaf,1,Leaf), 2, Leaf) *)
    (* nd is bound to 3 *)
    (* r is bound to Branch (Branch(Leaf,5,Leaf), 6, Branch(Leaf,7,Leaf)) *)
    let mem1 = add (prim(l,r),mem) in
    let mem2 = add (seco(l,r), mem1) in
    nd :: tree_traverse (peek mem2, pop mem2, prim, seco)

我们将左子分支l推入第一个堆栈mem1,并将左和右子分支推入堆栈mem2。然后,将3放在结果之前,并递归到堆栈mem2的顶部,同时放下它。

下一步,我们在第二个堆栈的顶部进行匹配,该堆栈包含右分支Branch (Branch(Leaf,5,Leaf), 6, Branch(Leaf,7,Leaf)),我们再次进入第二种情况, Branch(Leaf,5,Leaf)绑定到l变量,Branch(Leaf,7,Leaf)绑定到r。我们将6添加到结果中,然后按l,然后按并立即弹出r,然后递归到其中。

在递归的第三步,我们用Branch(Leaf,7,Leaf)进行调用,我们将7添加到结果中,并将左右Leaf推入堆栈。

在第四步,我们窥视Leaf节点,最后进入第一种情况,我们在其中检查堆栈,如果不为空,则递归到顶部。在我们的示例中,堆栈包含同级的左Leaf,然后是父节点的同级,等等。

您可以使用OCaml顶层(例如

#trace tree_traverse;;
tree_traverse (t,[],prim,seco);;

使用我上面提供的辅助函数的定义。