树到有尾递归的有序列表

时间:2018-12-15 19:44:13

标签: recursion ocaml tail-recursion

我实际上正坐在一个多小时的问题上,却没有找到解决方案。 我有这种数据类型: type 'a tree = Empty | Node of 'a * 'a tree * 'a tree

我必须找到一个函数,该函数可以在有序列表中转换给定的树。也没有不变性,就像左边的孩子必须小于右边的孩子。我已经找到了“常规”递归解决方案,但没有找到尾递归解决方案。我已经考虑过要建立一个无序列表,并用List.sort对其进行排序,但是这使用了不是尾递归的合并排序。也许有人有很好的建议。

谢谢!

1 个答案:

答案 0 :(得分:3)

如果要依次遍历树 并返回 list ,则意味着我们的函数inorder必须具有类型'a tree -> 'a list

let rec inorder t =
    match t with
      | Empty -> []
      | Node (v, l, r) -> List.append (inorder l) (v :: (inorder r)) (* ! *)

但是List.append处于尾部位置,而不是inorder。另一个问题是我们有{em>两个调用inorder。如果我们将inorder l放在尾巴位置,inorder r不可能在尾巴位置-反之亦然。

解决此问题的一种好方法是连续传递样式。我们将函数放在上面,并将其转换为带有附加参数的辅助函数,以供我们继续使用return

(* convert to helper function, add an extra parameter *)
let rec loop t return =
    match t with
        | Empty -> ...
        | Node (v, l, r) -> ...

延续表示“下一步做什么” ,因此,与其直接从我们的函数中发送值,不如将它们交给延续。这意味着在Empty情况下,我们将return []-而不是简单的[]

let rec loop t return =
    match t with
        | Empty -> return []
        | Node (v, l, r) -> ...

对于Node (v, l, r),现在有了一个额外的参数,我们可以编写自己的延续,通知loop接下来要做什么。因此,要构建我们的排序列表,我们需要先loop l,然后loop r(反之亦然),然后我们可以append let rec loop t return = match t with | Empty -> return [] | Node (v, l, r) -> loop l ... (* build the left_result *) loop r ... (* build the right_result *) return (List.append left_result (v :: right_result)) 。我们将像这样编写程序。

let rec loop t return =
    match t with
        | Empty -> return []
        | Node (v, l, r) ->
            loop l (fun left ->
            loop r (fun right ->
            return (List.append left (v :: right))))

在接下来的步骤中,我们将为延续部分填写实际的lambda语法。

inorder

最后,我们定义loop,这是对identity的调用,默认延续为let identity x = x let inorder t = let rec loop t return = match t with | Empty -> return [] | Node (v, l, r) -> loop r (fun right -> loop l (fun left -> return (List.append left (v :: right)))) in loop t identity

loop r (fun right -> ...)

您会看到Nodeloop l (fun left -> ...)分支的尾部位置。 List.append ...在第一个延续的尾部位置。并且List.append在第二个延续的尾部位置。如果inorder是尾递归过程,则List.append将不会增加堆栈。


请注意,使用Node对于大树来说可能是一个昂贵的选择。我们的函数每.Last.value调用一次。您能想到一种避免的方法吗?本练习留给读者。