二叉树的深度优先遍历和广度优先遍历
二叉樹是一種很重要的數據結構,對于二叉樹的遍歷,有深度優先遍歷和廣度優先遍歷,深度優先遍歷又有先序、中序、后續遍歷,廣度優先遍歷就是按層遍歷。
1. 深度優先遍歷
深度優先遍歷,也就是先序、中序、后續遍歷,我之前有一篇隨筆已經說的很清楚了,在這里我只貼下代碼就好了。
傳送門:詳細教你實現BST(二叉排序樹)
在這里我依然用之前建立好的Node、Stack、BST結構來實現代碼。
class Node {constructor(data, leftNode, rightNode) {this.data = datathis.leftNode = leftNodethis.rightNode = rightNode}print () {return this.data} }class Stack {constructor() {this.arr = []}pop () {return this.arr.shift()}push (data) {this.arr.unshift(data)}isEmpty () {return this.arr.length == 0} }class BST {constructor() {this.root = null}insert (data) {...}preOrder () {...}inOrder () {...}postOrder () {...}... }先是先序、中序、后序遍歷的遞歸實現,很簡單。
// 遞歸先序 function preOrderFn (node) {if (node) {console.log(node.print())preOrderFn(node.leftNode)preOrderFn(node.rightNode)} }// 遞歸中序 function inOrderFn (node) {if (node) {inOrderFn(node.leftNode)console.log(node.print())inOrderFn(node.rightNode)} }// 遞歸后續 function postOrderFn (node) {if (node) {postOrderFn (node.leftNode)postOrderFn (node.rightNode)console.log(node.print())} }然后就是先序、中序、后續遍歷的非遞歸實現了。詳細的解釋和說明,點擊上面的傳送門就有了,這里不過多贅述。
// 非遞歸先序 function PreOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()while (parentNode || !stack.isEmpty()) {// 一直遍歷到左子樹的最下面,一邊打印data,將一路遍歷過的節點push進棧中if (parentNode) {console.log(parentNode.data)stack.push(parentNode)parentNode = parentNode.leftNode}// 當parentNode為空時,說明已經達到了左子樹的最下面,可以出棧操作了else {parentNode = stack.pop()// 進入右子樹,開始新一輪循環parentNode = parentNode.rightNode}} }// 非遞歸中序 function inOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()while (parentNode || !stack.isEmpty()) {// 一直遍歷到左子樹的最下面,將一路遍歷過的節點push進棧中if (parentNode) {stack.push(parentNode)parentNode = parentNode.leftNode}// 當parentNode為空時,說明已經達到了左子樹的最下面,可以出棧操作了else {parentNode = stack.pop()console.log(parentNode.data)// 進入右子樹,開始新一輪循環parentNode = parentNode.rightNode}} }// 非遞歸后續 function PostOrderWithoutRecursion (root) {if (!root)returnvar parentNode = rootvar stack = new Stack()var lastVisitNode = nullwhile (parentNode || !stack.isEmpty()) {if (parentNode) {stack.push(parentNode)parentNode = parentNode.leftNode}else {parentNode = stack.pop()// 如果當前節點沒有右節點或者是右節點被訪問過,則訪問當前節點if (!parentNode.rightNode || parentNode.rightNode.data == lastVisitNode.data) {console.log(parentNode.data)lastVisitNode = parentNode}// 訪問右節點else {stack.push(parentNode)parentNode = parentNode.rightNodewhile (parentNode) {parentNode = parentNode.leftNode}}}} }2. 廣度優先遍歷
其實這片隨筆有點打醬油了,只說了兩個遍歷,還有一個是在以前實現過的。
廣度優先遍歷,顧名思義,就是橫向先遍歷,也就是按層次遍歷,從根節點往下,對每一層依此訪問,在每一層中從左到右(也可以從右到左)遍歷,遍歷完一層就進入下一層,直到沒有節點。
之前講深度優先非遞歸遍歷的時候,我們用到了一個棧的數據結構,到了廣度優先遍歷的時候,我們就要用到隊列這個數據結構。
為什么上一次用棧,這一次就要用到隊列了呢?
拿非遞歸中序遍歷舉例,我們每遍歷到一個節點就要進行入棧操作,遍歷完左節點之后,還需要找到根節點,再通過根節點找到右節點,所以我們需要最后遍歷到的節點在這個數據結構的最頂端,這不就是棧嗎?
先把我們的隊列的數據結構先建立起來再說。依然用數組模擬隊列的操作。
class Queue {constructor () {this.arr = []}enqueue (data) {return this.arr.push(data)}dequeue () {return this.arr.shift()}isEmpty () {return this.arr.length == 0} }為什么要用隊列呢,我們按層次遍歷,首先遍歷根節點,然后左子樹,右子樹,然后左子樹的左子樹,左子樹的右子樹,右子樹的左子樹,依此類推。每遍歷到一個節點,就將它存在一個數據結構里,先把它的前面的節點遍歷完,才能遍歷它,也就是一個先進先出(FIFO)的遍歷方式,這不就是隊列嗎?
說下思路:首先現將根節點做入隊操作,隊列里的節點表示我們要遍歷的節點,所以隊列為空的時候也就是沒有節點可以遍歷了,即隊列不為空的時候循環遍歷整個隊列。首先我們取出隊列的第一個節點,也就是對這個隊列做出隊操作,訪問這個節點的值,如果這個節點存在左子樹,那么將它的左子樹放在隊列的末尾,也就是對左子樹做入隊操作,右子樹同理。
思路很簡單,實現起來不難:
class BST {constructor() {this.root = null}// 廣度優先遍歷levelOrderTraversal () {levelOrderTraversalFn(this.root)}insert (data) {...}preOrder () {...}inOrder () {...}postOrder () {...}find (data) {..}getMax () {...}getMin () {...}deleteNode (data) {...}depth () {...}nodeCount () {...} }// 廣度優先遍歷 function levelOrderTraversalFn (node) {if(!node) {return}var que = new Queue()que.enqueue(node)while(!que.isEmpty()) {node = que.dequeue()console.log(node.data)if(node.leftNode) que.enqueue(node.leftNode)if(node.rightNode) que.enqueue(node.rightNode)} }我們試一下:
沒錯,那我們的廣度優先遍歷也就寫完了。
轉載于:https://www.cnblogs.com/isLiu/p/8328533.html
總結
以上是生活随笔為你收集整理的二叉树的深度优先遍历和广度优先遍历的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF实现Windows资源管理器(附源
- 下一篇: 简单多边形与圆交面积模板