日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

递归求二叉树的深度_优雅地用堆栈替代递归实现二叉树的深度优先搜索

發布時間:2023/12/15 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 递归求二叉树的深度_优雅地用堆栈替代递归实现二叉树的深度优先搜索 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文語言類型:JavaScript

有一個理論是“所有的遞歸都可以用堆棧實現”,道理大家都懂,實現起來怎么樣呢?

用js的前端開發者或許都不關心算法,本文嘗試用前端們熟悉的編碼形式,讓前端能更容易理解。我就從最簡單的二叉樹的深度優先搜索(DFS)入手。

數據結構定義

function Node(name) {this.name = name;this.left = null;this.right = null;this.setLeft = function(left) {this.left = left;return this;}this.setRight = function(right) {this.right = right;return this;}this.visit = function() {console.log(`visit ${this.name}`);} }

上面是一個樹節點對象的構造方法。每個對象的left和right分別指向自己的左右子節點,還有一個visit()方法,調用它就表示遍歷到這個節點了。

構造一棵二叉樹

根據上面的方法,我們就可以構造出一棵簡單的二叉樹了:

let a = new Node('a') let b = new Node('b') let c = new Node('c') let d = new Node('d') let e = new Node('e') let f = new Node('f') let g = new Node('g') let h = new Node('h')a.setLeft(b).setRight(f) b.setLeft(c).setRight(d) d.setLeft(e) f.setLeft(g).setRight(h)

遞歸實現法

遞歸的實現就很簡單了,這里不用解釋:

//recursive function dfs1(node) {if(!node) return;node.visit()dfs1(node.left)dfs1(node.right) }dfs1(a)

運行結果:

visit a visit b visit c visit d visit e visit f visit g visit h

轉成堆棧模式

遞歸函數實際上在編譯器內部維護了一個隱藏的工作棧。遞歸發生一次,就進行一次進棧、遞歸結束一次,就進行一次彈棧。當這個棧變成空的時候,也是整個遞歸函數結束的時候。

以上遞歸函數dfs1的特征是:

  • 1.所有的操作都在棧頂的這個節點上進行,首先訪問這個節點,此時是這個節點第一次出現在棧頂;
  • 2.如果當前節點有左子節點,就壓入它的左子節點入棧,當左子節點處理完畢彈出時,當前節點又回到棧頂;
  • 3.當前節點第二次回到棧頂時,就要考慮右子節點了,壓入右子節點入棧,當左子節點處理完畢彈出時,當前節點第三次回到棧頂;

當前節點第三次回到棧頂時,表示本節點處理完畢,可以彈出了,本節點使命結束,轉而處理它的父節點。

遞歸轉非遞歸的難點是判斷何時彈棧。經過以上分析,無非是以上三個步驟都處理完畢時——也就是節點第三次出現在棧頂時、也就是dfs1中的三行語句都執行完畢時——彈棧。

那么,可以設計一個數據結構:{ node, rest: 3 },模擬dfs1的作用域保存在堆棧中,每當這個作用域出現在棧頂一次,rest自減1,當rest為0時,這個作用域就可以彈出了。

//use stacks function dfs2(node) {if(!node) return;let stack = []stack.push(__makeScope(node))while(true) {let Scope = stack[stack.length - 1]let current = Scope.nodeif(Scope.rest === 3) {Scope.rest--current.visit()} else if(Scope.rest === 2) {Scope.rest--if(current.left) {stack.push(__makeScope(current.left))}} else if(Scope.rest === 1) {Scope.rest--if(current.right) {stack.push(__makeScope(current.right))}} else/* if(Scope.rest === 0)*/ {stack.pop()if(stack.length === 0) break;}}function __makeScope(node) {return { node, rest: 3 }} }dfs2(a)

性能分析

當樹節點比較少的時候,兩個函數性能差別不大,但是當節點數達到10萬數量級時(老舊瀏覽器會更低),dfs1直接"stack overflow",dfs2依然堅挺。從這里或許也能看出遞歸的弱點。

引申

這里先考慮了二叉樹,每個節點的處理次數為3,那么n叉樹的情況下,每個節點處理次數就是n+1,以上程序還是很方便改寫的,無非就是子樹用數組來存儲。這個以后有時間再寫。

本文為原創,轉載請注明出處

總結

以上是生活随笔為你收集整理的递归求二叉树的深度_优雅地用堆栈替代递归实现二叉树的深度优先搜索的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。