【问题标题】:OCaml binary tree depth with no stack overflow无堆栈溢出的 OCaml 二叉树深度
【发布时间】:2014-01-17 10:37:56
【问题描述】:

我有以下实现二叉树和一个深度函数来计算它的深度:

type 'a btree =
| Empty
| Node of 'a * 'a btree * 'a btree;;

let rec depth t = match t with
| Empty -> 0
| Node (_, t1, t2) -> 1 + Int.max (depth t1)  (depth t2)

这里的问题是“深度”是递归的,当树太大时会导致堆栈溢出。

我读到了尾递归以及编译器如何将其优化为 while 循环以删除堆栈调用。

你会如何让这个函数尾递归或者让它使用 while/for 循环来代替?

【问题讨论】:

  • 将其转换为尾递归或迭代算法需要使用显式堆栈。这是一个真正的问题还是只是一个练习?因为在实际应用中,您要么确保树是平衡的,因此递归不会太深,要么在构造节点时将深度存储在节点中。
  • 感谢您为实际应用提供的提示。不过,这是一个练习。

标签: recursion ocaml binary-tree tail-recursion


【解决方案1】:
type 'a btree =
| Empty
| Node of 'a * 'a btree * 'a btree;;

let max x y = if x > y then x else y

let depth t =
  let rec dep m = function (* d records current level, m records max depth so far *)
    | [] -> m
    | (Empty,d)::tl -> dep (max m d) tl
    | (Node (_,l,r),d)::tl -> dep (max m d) ((l,d+1)::(r,d+1)::tl)
  in 
  dep 0 [(t,0)]

基本上,你需要三样东西:

  1. 沿路径存储节点的列表(堆栈)
  2. 记录当前深度的指示器
  3. 目前的最大深度

每当我们遇到需要消除可能的 stackoverflow 问题时,我们应该考虑两件事:tail-recursiveexplicit stack

对于尾递归,您必须找到一种方法来显式存储通过每个递归步骤生成的临时数据。

对于显式堆栈,请记住递归可以工作的原因是因为它在内部使用了一个大小有限的堆栈。如果我们分析逻辑并使堆栈显式化,那么我们就不再需要那个内部堆栈了。

【讨论】:

  • Hmm.. 好像不行:给定:Node (10, Node (5, Empty, Empty), Node (15, Empty, Empty)) 它返回 7
  • 这很聪明(至少对我而言),但我必须重读几次才能理解它。非常感谢!
  • 另一个提示是 ocaml/FP 非常方便地将信息组合在一起,基于临时。像我一样,记录每个节点的级别信息d
  • @Jackson_Tale 所以基本上显式堆栈就像一个临时存储来存储需要处理的节点,一旦你处理了一个节点,你就删除它并将它的子节点添加到堆栈中,然后当你到达一个 Empty (一片叶子)你刚刚删除,这就是你最终得到一个空堆栈 [] 的方式,你只会返回当前的最大值(m)。对吗?
  • 哦,我现在明白了,您基本上是检查、删除、添加子项。当它碰到叶子时,循环最终停止添加更多。每次点击一个节点时,您都​​会检查它是否具有比当前最大值更高的级别并更新最大值。最终你会返回最终的最大值
【解决方案2】:

在务实的情况下,解决方案是使用平衡树,它将深度限制为 log(n) 的某个倍数。即使 n 非常大,log(n) 也足够小,不会耗尽堆栈空间。

否则请参阅 Kadaku 链接的 SO 页面。这个问题的答案非常好。

【讨论】:

    【解决方案3】:

    我已经回答过一次类似的问题。转发解决方案:

    使用fold_tree 和 CPS 有一个简洁通用的解决方案 - 连续传递样式:

    let fold_tree tree f acc =
      let loop t cont =
        match tree with
        | Leaf -> cont acc
        | Node (x, left, right) ->
          loop left (fun lacc ->
            loop right (fun racc ->
              cont @@ f x lacc racc))
      in loop tree (fun x -> x)
    
    let depth tree = fold_tree tree (fun x dl dr -> 1 + (max dl dr)) 0
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-12-07
      • 1970-01-01
      • 2021-10-16
      • 2013-08-10
      • 2012-05-10
      • 2017-04-24
      • 2011-11-24
      • 2017-09-29
      相关资源
      最近更新 更多