二叉树是由根节点,左子树,右子树构成,左子树和友子树分别是一个二叉树。
这篇文章主要在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
}
}
}
广度优先遍历
广度优先遍历是从二叉树的第一层(根结点)最先,自上至下逐层遍历;在统一层中,根据从左到右的递次对结点一一接见。
完成:
<!–more–>
运用数组模仿行列。首先将根节点归入行列。当行列不为空的时刻,实行轮回:掏出行列的一个节点,假如该结点的左子树为非空,则将该结点的左子树入行列;假如该结点的右子树为非空,则将该结点的右子树入行列。
(形貌有点不清晰,直接看代码吧。)
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
}