二叉树的四种遍历方式:
二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被访问依次且仅被访问一次。
四种遍历方式分别为:先序遍历、中序遍历、后序遍历、层序遍历。树的遍历一般采用递归的方式更加的简介易懂,二叉树的递归遍历很好写,也很好理解。但因为是递归程序,不可避免地需要调用系统栈,耗时较长,这里我们也来探究一下二叉树的非递归遍历的算法,所以下面的介绍中会将递归算法和非递归算法一起介绍
二叉树的可以用顺序存储方式和链式存储方法,其实顺序存储方法一般用于树中空缺结点比较少的树,如完全二叉树等,所以这里采用更具一般性的链式存储结构。
function BTNode(left, right, element) {
this.left = left;
this.right = right;
this.element = element;
}
二叉树先序遍历:
二叉树先序遍历的实现思想是:
访问根节点;
访问当前节点的左子树;
若当前节点无左子树,则访问当前节点的右子树;
递归方法:
function preOrderTraverse(BT) {
if (BT) {
console.log(BT.data);
preOrderTraverse(BT.left);
preOrderTraverse(BT.right);
}
}
非递归方法
非递归的方法需要采用栈,这是由于我们在先序访问一个结点时,先访问了它的根结点,然后去访问左子树,在我们访问左子树的时候我们就丢失了去访问右子树的途径,所以我们需要用一种方法去保存根结点,确保在左子树访问完需要访问右子树的时候,能通过访问根来访问右子树。而JS我们可以很方便的用数组的两个方法push和pop实现一个简易的栈结构。
1、入栈前,先输出本节点信息;
2、先左子树一次入栈,直到没有左子树的结点为止;
3、出栈栈顶元素,然后,找到右子树,继续①②操作,直到栈为空 且 结点为空。
function preOrderT(BT)//非递归先序遍历
{
let Stack = [];
let p = BT;
while (p || Stack.length != 0) {
while (p) {
Stack.push(p);
console.log(p.data);
p = p.left;
}
if (Stack.length != 0) {
p = Stack.pop();
p = p.right;
}
}
}
二叉树中序遍历:
二叉树中序遍历的实现思想是:
访问当前节点的左子树;
访问根节点;
访问当前节点的右子树;
递归方法:
function inOrderTraverse(BT) {
if (BT) {
inOrderTraverse(BT.left);
console.log(BT.data);
inOrderTraverse(BT.right);
}
}
非递归方法
类似于先序遍历,但是由于中序遍历的时候我们在第一次遇到结点时,我们要先对它的左子树进行访问,然后在对根结点进行访问,所以我们需要在出栈的时候对结点进行访问。
1、先左子树一次入栈,直到没有左子树的结点为止;
2、出栈栈顶元素,然后输出结点信息,找到右子树,继续①②操作,直到栈为空 且 结点为空。
function preOrderT(BT)//非递归先序遍历
{
let Stack = [];
let p = BT;
while (p || Stack.length != 0) {
while (p) {
Stack.push(p);
p = p.left;
}
if (Stack.length != 0) {
p = Stack.pop();
console.log(p.data);
p = p.right;
}
}
}
二叉树后序遍历
二叉树中序遍历的实现思想是:
访问当前节点的左子树;
访问当前节点的右子树;
访问根节点;
递归方法:
function lastOrderTraverse(BT) {
if (BT) {
lastOrderTraverse(BT.left);
lastOrderTraverse(BT.right);
console.log(BT.data);
}
}
非递归方法
后序遍历是二叉树三种遍历的非递归算法中最难实现的一种,因为在我们访问根结点时我们需要考虑两种情况:
1、右子树访问过了或者右子树为空,在访问根结点
2、右子树没有被访问过,先访问右子树
由于我们在访问更结点前访问的节点要不是左子树的根结点(右子树为空),要不是右子树的根结点,所以我们可以设置一个标志tag来标识上一步访问的节点,如果右子树为空,则访问根结点,如果不为空在根据tag看是否与右子树相同,来判断是否被访问过,如果被访问过,则可访问该节点,否则访问右子树。
function lastOrderT(BT)//非递归后序遍历
{
let Stack = [];
let p = BT;
let tag;
while (p || Stack.length != 0) {
if (p) {
Stack.push(p);
p = p.left;
}
else {
p = Stack[Stack.length - 1];
if (p.right && tag != p.right) {
p = p.right;
}
else {
Stack.pop();
console.log(p.data);
tag = p;
p = null;
}
}
}
}
二叉树的层次遍历
要进行层次遍历,需要建立一个循环队列。先将二叉树头结点入队列,然后出队列,访问该结点,如果它有左子树,则将左子树的根结点入队:如果它有右子树,则将右子树的根结点入队。然后出队列,对出队结点访问,如此反复,直到队列为空为止。
function levelOrderTraverse(BT) {//层次遍历树
let Queue = [];
Queue.push(BT);
let p;
while (Queue.length != 0) {
p = Queue.shift();
console.log(p.data);
if (p.left != null) {
Queue.push(p.left);
}
if (p.right != null) {
Queue.push(p.right);
}
}
}