簡體   English   中英

OCaml二叉樹深度,無堆棧溢出

[英]OCaml binary tree depth with no stack overflow

我對二進制樹和深度函數執行以下實現,以計算其深度:

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循環?

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問題的問題時,我們都應該考慮兩件事: 尾遞歸顯式stack

對於尾遞歸,您必須找到一種方法來顯式存儲通過每個遞歸步驟生成的臨時數據。

對於顯式堆棧,請記住遞歸可以工作的原因是因為它在內部使用了有限大小的堆棧。 如果我們分析邏輯並使該堆棧明確,那么我們就不再需要該內部堆棧。

在實際情況下,解決方案是使用平衡樹,該樹將深度限制為log(n)的某些倍數。 即使對於非常大的n,log(n)也足夠小,以至於不會耗盡堆棧空間。

否則,請參閱由Kadaku鏈接的SO頁。 它對這個問題有很好的答案。

我已經回答過類似的問題一次。 重新發布解決方案:

有一個使用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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM