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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

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

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

本文語言類型:JavaScript

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

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

數(shù)據(jù)結構定義

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}`);} }

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

構造一棵二叉樹

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

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)

遞歸實現(xiàn)法

遞歸的實現(xiàn)就很簡單了,這里不用解釋:

//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

轉成堆棧模式

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

以上遞歸函數(shù)dfs1的特征是:

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

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

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

那么,可以設計一個數(shù)據(jù)結構:{ node, rest: 3 },模擬dfs1的作用域保存在堆棧中,每當這個作用域出現(xiàn)在棧頂一次,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)

性能分析

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

引申

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

本文為原創(chuàng),轉載請注明出處

總結

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

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