[英]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)]
基本上,您需要三件事:
每當我們遇到需要消除可能的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.