二叉树是一种比较特殊的树型数据结构,这一次来学习一下如何递归创建二叉树,以及如何递归遍历二叉树,同时最后以前序遍历为例子,非递归遍历二叉树。
要写出代码来,要心中有数,二叉树的结构相较于前面的链表、队列、栈要复杂些,所以,在没底的时候多画图来理解。
由上图可知二叉树的基本结构是一个数据域和两个指针域,^代表为null,因此我们创建二叉树的结点构造函数如下:
//内部类,保存左右指针和数据域
function TreeNode(data){
this.data=data;
this.left=null;
this.right=null;
}
一、递归创建二叉树
对于上面这一个二叉树来说,如果采用递归的方式来创建,你可以采用一个一个结点的方式来创建,不过比较麻烦,因为你还要花时间精力判断新结点需要插在哪里,如果你采用左子树小于右子树的方式去插入,那或许简单点,这种二叉树就是后面会讲到的“二叉排序树”,这一次我们创建的就是一颗普通的二叉树,所以我们根据二叉树的前序遍历结果来构造二叉树。
上图的二叉树前序遍历结果就是:AB#D##C##,#代表为空结点。
如果我们知道了一颗二叉树的前序遍历结果,如何构造这个二叉树呢?很简单,以递归的方式来说:根据字符串AB#D##C##,第一个创建A结点,创建后递归创建A结点的left结点B,再递归得到一个“#”即空结点,空姐点直接返回到B结点,所以B的left是null,再递归B的右结点,得到D,再依次递归D的左右子树,均是null,所以一层一层返回,知道返回到根节点,再访问根节点的右子树,由此一颗二叉树构造完成。
代码演示:
BinaryTree.prototype.createTree=function(node){
let index=wm.get(this)[this.index]++; //index私有变量保持对字符串的索引
const data=this.treeStr.split('')[index];
if(index<this.treeStr.split('').length){
if (data === '#') {
node = null;
} else {
node = new TreeNode(data);
node.left = this.createTree(node.left);
node.right = this.createTree(node.right);
}
}
return node;
}
二、返回二叉树深度
返回深度的算法同样采用递归实现,先递归树的左子树,当左子树为空时,判断是否有右子树,有就去递归,否则返回0,通过i变量去检测左子树,j检测右子树,返回max(i,j)+1
代码实现:
/**
* 返回二叉树深度
* */
BinaryTree.prototype.treeDepth=function(tree){
let i,j;
if(!tree){
return 0;
}
if(tree.left){
i=this.treeDepth(tree.left);
}else{
i=0;
}
if(tree.right){
j=this.treeDepth(tree.right);
}else{
j=0;
}
return i>j ? i+1 : j+1;
}
三、非递归前序遍历
非递归遍历,采用一个数组栈去存储结点的引用,以前序为例:前序遍历是根左右,所以先将A结点入栈,再输出A结点的data,再不断遍历左子树,遍历一个结点就将这个结点入栈并输出,直到左子树为空,此时遍历结点的变量也一定是null,所以此时出栈,也就是退一格到上一个结点,此时去遍历该结点的右子树,不管为不为空,如果为空就再次出栈至倒数第二个结点,期间一直是一个循环控制,当元素全部出栈或树为空则循环结束。
代码演示:
/**
* 非递归前序查找结点
* */
BinaryTree.prototype.preTraversalByStack=function(tree){
let stack=new Array(); //初始化一个栈数组
while(stack.length>0 || tree){
while(tree){ //当左子树不为空一直入栈并输出
console.log(tree.data);
stack.unshift(tree);
tree=tree.left;
}
/**
* 左子树完成遍历后,出栈至上一个引用查找是否含有右子树
* */
if(stack.length>0){
tree=stack.shift();
tree=tree.right;
}
}
}
递归遍历算法简单,不再赘述。
完整源码:
'use strict'
//二叉树
const BinaryTree=function(){
const wm=new WeakMap();
//二叉树构造函数
function BinaryTree(treeStr=""){
this.treeStr=treeStr;
this.index=Symbol('private');
const privateMembers=wm.get(this) || {};
privateMembers[this.index]=0;
wm.set(this,privateMembers);
const node=null;
this.tree=this.createTree(node);
}
//内部类,保存左右指针和数据域
function TreeNode(data){
this.data=data;
this.left=null;
this.right=null;
}
/**
* 递归创建树
* 对于#视为null
* */
BinaryTree.prototype.createTree=function(node){
let index=wm.get(this)[this.index]++;
const data=this.treeStr.split('')[index];
if(index<this.treeStr.split('').length){
if (data === '#') {
node = null;
} else {
node = new TreeNode(data);
node.left = this.createTree(node.left);
node.right = this.createTree(node.right);
}
}
return node;
}
/**
* 返回二叉树深度
* */
BinaryTree.prototype.treeDepth=function(tree){
let i,j;
if(!tree){
return 0;
}
if(tree.left){
i=this.treeDepth(tree.left);
}else{
i=0;
}
if(tree.right){
j=this.treeDepth(tree.right);
}else{
j=0;
}
return i>j ? i+1 : j+1;
}
/**
* 二叉树前序遍历
* */
BinaryTree.prototype.preTraversal=function(tree){
if(tree){
console.log(tree.data);
tree.left=this.preTraversal(tree.left);
tree.right=this.preTraversal(tree.right);
}
return tree;
}
/**
* 中序遍历
* */
BinaryTree.prototype.midTraversal=function(tree){
if (tree) {
tree.left =this.midTraversal(tree.left);
console.log(tree.data);
tree.right=this.midTraversal(tree.right);
}
return tree;
}
/**
* 后序遍历
* */
BinaryTree.prototype.postTraversal=function(tree){
if (tree) {
tree.left = this.postTraversal(tree.left);
tree.right = this.postTraversal(tree.right);
console.log(tree.data);
}
return tree;
}
/**
* 非递归前序查找结点
* */
BinaryTree.prototype.preTraversalByStack=function(tree){
let stack=new Array(); //初始化一个栈数组
while(stack.length>0 || tree){
while(tree){ //当左子树不为空一直入栈并输出
console.log(tree.data);
stack.unshift(tree);
tree=tree.left;
}
/**
* 左子树完成遍历后,出栈至上一个引用查找是否含有右子树
* */
if(stack.length>0){
tree=stack.shift();
tree=tree.right;
}
}
}
return BinaryTree;
}();
const tree=new BinaryTree('ABDH#K###E##CFI###G#J##');
console.log(tree);
tree.postTraversal(tree.tree);
console.log(tree.treeDepth(tree.tree));
tree.preTraversalByStack(tree.tree);