摘要:一個二叉樹的例子廣度優先遍歷廣度優先遍歷是從二叉樹的第一層根結點開始,自上至下逐層遍歷在同一層中,按照從左到右的順序對結點逐一訪問。有的書里將二叉樹的遍歷只講了上面三種遞歸遍歷。
二叉樹是由根節點,左子樹,右子樹組成,左子樹和友子樹分別是一個二叉樹。
這篇文章主要在JS中實現二叉樹的遍歷。
var tree = {
value: 1,
left: {
value: 2,
left: {
value: 4
}
},
right: {
value: 3,
left: {
value: 5,
left: {
value: 7
},
right: {
value: 8
}
},
right: {
value: 6
}
}
}
廣度優先遍歷
廣度優先遍歷是從二叉樹的第一層(根結點)開始,自上至下逐層遍歷;在同一層中,按照從左到右的順序對結點逐一訪問。
實現:
使用數組模擬隊列。首先將根節點歸入隊列。當隊列不為空的時候,執行循環:取出隊列的一個節點,如果該結點的左子樹為非空,則將該結點的左子樹入隊列;如果該結點的右子樹為非空,則將該結點的右子樹入隊列。
(描述有點不清楚,直接看代碼吧。)
var levelOrderTraversal = function(node) {
if(!node) {
throw new Error("Empty Tree")
}
var que = []
que.push(node)
while(que.length !== 0) {
node = que.shift()
console.log(node.value)
if(node.left) que.push(node.left)
if(node.right) que.push(node.right)
}
}
遞歸遍歷
覺得用這幾個字母表示遞歸遍歷的三種方法不錯:
D:訪問根結點,L:遍歷根結點的左子樹,R:遍歷根結點的右子樹。
先序遍歷:DLR
中序遍歷:LDR
后序遍歷:LRD
順著字母表示的意思念下來就是遍歷的順序了 ^ ^
這3種遍歷都屬于遞歸遍歷,或者說深度優先遍歷(Depth-First Search,DFS),因為它總
是優先往深處訪問。
var preOrder = function (node) {
if (node) {
console.log(node.value);
preOrder(node.left);
preOrder(node.right);
}
}
中序遍歷的遞歸算法:
var inOrder = function (node) {
if (node) {
inOrder(node.left);
console.log(node.value);
inOrder(node.right);
}
}
后序遍歷的遞歸算法:
var postOrder = function (node) {
if (node) {
postOrder(node.left);
postOrder(node.right);
console.log(node.value);
}
}
非遞歸深度優先遍歷
其實對于這些概念誰是屬于誰的我也搞不太清楚。有的書里將二叉樹的遍歷只講了上面三種遞歸遍歷。有的分廣度優先遍歷和深度優先遍歷兩種,把遞歸遍歷都分入深度遍歷當中;有的分遞歸遍歷和非遞歸遍歷兩種,非遞歸遍歷里包括廣度優先遍歷和下面這種遍歷。個人覺得怎么分其實并不重要,掌握方法和用途就好 :)
剛剛在廣度優先遍歷中使用的是隊列,相應的,在這種不遞歸的深度優先遍歷中我們使用棧。在JS中還是使用一個數組來模擬它。
這里只說先序的:
額,我嘗試了描述這個算法,然而并描述不清楚。按照代碼走一邊你就懂了。(認真臉)
var preOrderUnRecur = function(node) {
if(!node) {
throw new Error("Empty Tree")
}
var stack = []
stack.push(node)
while(stack.length !== 0) {
node = stack.pop()
console.log(node.value)
if(node.right) stack.push(node.right)
if(node.left) stack.push(node.left)
}
}
看了LK的這一篇,找到了非遞歸后序的算法(之前沒寫就是因為這種實在不會啊啊?。栽谶@里把非遞歸的遍歷方法補充完整。
非遞歸中序
先把數的左節點推入棧,然后取出,再推右節點。(我能說出的描述就如此了~~)
var inOrderUnRecur = function(node) {
if(!node) {
throw new Error("Empty Tree")
}
var stack = []
while(stack.length !== 0 || node) {
if(node) {
stack.push(node)
node = node.left
} else {
node = stack.pop()
console.log(node.value)
node = node.right
}
}
}
非遞歸后序(使用一個棧)
這里使用了一個臨時變量記錄上次入棧/出棧的節點。思路是先把根節點和左樹推入棧,然后取出左樹,再推入右樹,取出,最后取跟節點。
var posOrderUnRecur = function(node) {
if(!node) {
throw new Error("Empty Tree")
}
var stack = []
stack.push(node)
var tmp = null
while(stack.length !== 0) {
tmp = stack[stack.length - 1]
if(tmp.left && node !== tmp.left && node !== tmp.right) {
stack.push(tmp.left)
} else if(tmp.right && node !== tmp.right) {
stack.push(tmp.right)
} else {
console.log(stack.pop().value)
node = tmp
}
}
}
非遞歸后序(使用兩個棧)
這個算法的思路和上面那個差不多,s1有點像一個臨時變量。
var posOrderUnRecur = function(node) {
if(node) {
var s1 = []
var s2 = []
s1.push(node)
while(s1.length !== 0) {
node = s1.pop()
s2.push(node)
if(node.left) {
s1.push(node.left)
}
if(node.right) {
s1.push(node.right)
}
}
while(s2.length !== 0) {
console.log(s2.pop().value);
}
}
}
Morris遍歷
這個方法即不用遞歸也不用棧實現三種深度遍歷,空間復雜度為O(1)(這個概念我也不是特別清楚org)
(這三種算法我先放著,有空再研究)
Morris先序:
var morrisPre = function(head) {
if(!head) {
return
}
var cur1 = head,
cur2 = null
while(cur1) {
cur2 = cur1.left
if(cur2) {
while(cur2.right && cur2.right != cur1) {
cur2 = cur2.right
}
if(!cur2.right) {
cur2.right = cur1
console.log(cur1.value)
cur1 = cur1.left
continue
} else {
cur2.right = null
}
} else {
console.log(cur1.value)
}
cur1 = cur1.right
}
}
Morris中序:
var morrisIn = function(head) {
if(!head) {
return
}
var cur1 = head,
cur2 = null
while(cur1) {
cur2 = cur1.left
if(cur2) {
while(cur2.right && cur2.right !== cur1) {
cur2 = cur2.right
}
if(!cur2.right) {
cur2.right = cur1
cur1 = cur1.left
continue
} else {
cur2.right = null
}
}
console.log(cur1.value)
cur1 = cur1.right
}
}
Morris后序:
var morrisPost = function(head) {
if(!head) {
return
}
var cur1 = head,
cur2 = null
while(cur1) {
cur2 = cur1.left
if(cur2) {
while(cur2.right && cur2.right !== cur1) {
cur2 = cur2.right
}
if(!cur2.right) {
cur2.right = cur1
cur1 = cur1.left
continue
} else {
cur2.right = null
printEdge(cur1.left)
}
}
cur1 = cur1.right
}
printEdge(head)
}
var printEdge = function(head) {
var tail = reverseEdge(head)
var cur = tail
while(cur) {
console.log(cur.value)
cur = cur.right
}
reverseEdge(tail)
}
var reverseEdge = function(head) {
var pre = null,
next = null
while(head) {
next = head.right
head.right = pre
pre = head
head = next
}
return pre
}
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.hztianpu.com/yun/78922.html
摘要:同樣結點樹的二叉樹,完全二叉樹的深度最小。二叉樹每個結點最多有兩個孩子,所以為它設計一個數據域和兩個指針域是比較自然的想法,我們稱這樣的鏈表叫做二叉鏈表。 二叉樹的概念 二叉樹(Binary Tree)是n(n>=0)個結點的有限集合,該集合或者為空集(空二叉樹),或者由一個根結點和兩棵互不相交的、分別稱為根結點的左子樹和右子樹的二叉樹組成。 showImg(https://seg...
摘要:樹中結點的最大層次稱為樹的深度或高度。二叉樹有深度遍歷和廣度遍歷,深度遍歷有前序中序和后序三種遍歷方法。二叉樹的前序遍歷可以用來顯示目錄結構等中序遍歷可以實現表達式樹,在編譯器底層很有用后序遍歷可以用來實現計算目錄內的文件及其信息等。 樹的簡介 棧、隊列、鏈表等數據結構,都是順序數據結構。而樹是非順序數據結構。樹型結構是一類非常重要的非線性結構。直觀地,樹型結構是以分支關系定義的層次結...
摘要:因此,根據題目給出的先序遍歷和中序遍歷,可以畫出二叉樹選參考數據結構與算法描述實現二叉樹算法淺談數據結構二叉樹慕課網實現二叉樹算法前端樹控件騰訊軟件開發面試題 內容銜接上一章 數據結構與算法:常見排序算法 內容提要 什么是樹 - 為什么使用樹 二叉樹 二叉查找樹 紅黑樹 B、B+樹 堆 伸展樹 樹 可以點擊鏈接感受下筆者用d3.js畫的tree https://codepen...
摘要:前言可能有一部分人沒有讀過我上一篇寫的二叉堆,所以這里把二叉樹的基本概念復制過來了,如果讀過的人可以忽略前面針對二叉樹基本概念的介紹,另外如果對鏈表數據結構不清楚的最好先看一下本人之前寫的數據結構鏈表二叉樹二叉樹是一種樹形結構,它的特點是 前言 可能有一部分人沒有讀過我上一篇寫的二叉堆,所以這里把二叉樹的基本概念復制過來了,如果讀過的人可以忽略前面針對二叉樹基本概念的介紹,另外如果對鏈...
閱讀 1645·2023-04-25 16:31
閱讀 2250·2021-11-24 10:33
閱讀 2937·2021-09-23 11:33
閱讀 2841·2021-09-23 11:31
閱讀 3246·2021-09-08 09:45
閱讀 2672·2021-09-06 15:02
閱讀 2815·2019-08-30 14:21
閱讀 2552·2019-08-30 12:56