這篇文章主要為大家詳細(xì)介紹了JS中的二叉樹遍歷,何為二叉樹,什么是二叉樹的遍歷,感興趣的小伙伴們可以參考一下
二叉樹是由根節(jié)點(diǎn),左子樹,右子樹組成,左子樹和友子樹分別是一個(gè)二叉樹。
這篇文章主要在JS中實(shí)現(xiàn)二叉樹的遍歷。
一個(gè)二叉樹的例子
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
}
}
}
廣度優(yōu)先遍歷
廣度優(yōu)先遍歷是從二叉樹的第一層(根結(jié)點(diǎn))開始,自上至下逐層遍歷;在同一層中,按照從左到右的順序?qū)Y(jié)點(diǎn)逐一訪問。
實(shí)現(xiàn):
<!--more-->
使用數(shù)組模擬隊(duì)列。首先將根節(jié)點(diǎn)歸入隊(duì)列。當(dāng)隊(duì)列不為空的時(shí)候,執(zhí)行循環(huán):取出隊(duì)列的一個(gè)節(jié)點(diǎn),如果該結(jié)點(diǎn)的左子樹為非空,則將該結(jié)點(diǎn)的左子樹入隊(duì)列;如果該結(jié)點(diǎn)的右子樹為非空,則將該結(jié)點(diǎn)的右子樹入隊(duì)列。
(描述有點(diǎn)不清楚,直接看代碼吧。)
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)
}
}
遞歸遍歷
覺得用這幾個(gè)字母表示遞歸遍歷的三種方法不錯(cuò):
D:訪問根結(jié)點(diǎn),L:遍歷根結(jié)點(diǎn)的左子樹,R:遍歷根結(jié)點(diǎn)的右子樹。
先序遍歷:DLR
中序遍歷:LDR
后序遍歷:LRD
順著字母表示的意思念下來就是遍歷的順序了 ^ ^
這3種遍歷都屬于遞歸遍歷,或者說深度優(yōu)先遍歷(Depth-First Search,DFS),因?yàn)樗?/P>
是優(yōu)先往深處訪問。
先序遍歷的遞歸算法:
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);
}
}
非遞歸深度優(yōu)先遍歷
其實(shí)對(duì)于這些概念誰是屬于誰的我也搞不太清楚。有的書里將二叉樹的遍歷只講了上面三種遞歸遍歷。有的分廣度優(yōu)先遍歷和深度優(yōu)先遍歷兩種,把遞歸遍歷都分入深度遍歷當(dāng)中;有的分遞歸遍歷和非遞歸遍歷兩種,非遞歸遍歷里包括廣度優(yōu)先遍歷和下面這種遍歷。個(gè)人覺得怎么分其實(shí)并不重要,掌握方法和用途就好 :)
剛剛在廣度優(yōu)先遍歷中使用的是隊(duì)列,相應(yīng)的,在這種不遞歸的深度優(yōu)先遍歷中我們使用棧。在JS中還是使用一個(gè)數(shù)組來模擬它。
這里只說先序的:
額,我嘗試了描述這個(gè)算法,然而并描述不清楚,按照代碼走一邊你就懂了。
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)
}
}
看了這一篇,找到了非遞歸后序的算法,所以在這里把非遞歸的遍歷方法補(bǔ)充完整。
非遞歸中序
先把數(shù)的左節(jié)點(diǎn)推入棧,然后取出,再推右節(jié)點(diǎn)。
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
}
}
}
非遞歸后序(使用一個(gè)棧)
這里使用了一個(gè)臨時(shí)變量記錄上次入棧/出棧的節(jié)點(diǎn)。思路是先把根節(jié)點(diǎn)和左樹推入棧,然后取出左樹,再推入右樹,取出,最后取跟節(jié)點(diǎn)。
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
}
}
}
非遞歸后序(使用兩個(gè)棧)
這個(gè)算法的思路和上面那個(gè)差不多,s1有點(diǎn)像一個(gè)臨時(shí)變量。
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遍歷
這個(gè)方法即不用遞歸也不用棧實(shí)現(xiàn)三種深度遍歷,空間復(fù)雜度為O(1)(這個(gè)概念我也不是特別清楚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) {
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。