JS实现二叉树算法

本文通过js来认识什么是二叉树、如何构建二叉树、二叉树有哪几种遍历算法及节点删除操作实现

什么是二叉树

二叉树是n个结点的有限集合,该集合或者为空集,或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树的二叉树组成。如下图1为二叉树:

JS构建二叉树

约定右子树的节点值都大于左子树节点的值(也可以是左子树节点值大于右子树节点值)。数组第一个元素作为根节点的值、根据数组元素的顺序构建二叉树。首先通过类来定义二叉树的,这个类具有节点的节点结构属性、根节点属性及节点插入方法。

//二叉树
function BinaryTree() {
    
    // 节点结构
	var Node = function(key){
		this.key = key;
		this.left = null;
		this.right = null;
	};

    //根节点
	var root = null;

    //新增节点
    var insertNode = function(node,newNode){
    	//约定右孩子都大于左孩子节点
    	if(newNode.key<node.key){
    		if(node.left === null){//没有左孩子,则新增左孩子
    			node.left = newNode;
    		}else{
    			//如果有左孩子则递归算法实现插入左孩子节点
    			insertNode(node.left,newNode);
    		}
    	}else {
    		//如果有孩子为null,则新增右孩子
    		if(node.right===null){
    			node.right = newNode;
    		}else{
    			//如果有左孩子则递归算法实现插入右孩子节点
    			insertNode(node.right,newNode);
    		}
    	}
    };
    // 插入新节点
	this.insert =function(key){
		//创建节点
       var node = new Node(key);
       if(root === null){//判断是否为根节点
       	 root = node;
       }else {
       	// 不是根节点则新增节点
       	 insertNode(root,node);
       }
	};
}

图1的二叉树的实现

//构建二叉树
var nodes = [6,2,3,4,9,8,7,12,1,22]
var binaryTree = new BinaryTree();
nodes.forEach(function(key){
    binaryTree.insert(key);
});

二叉树的几种遍历算法

  • 如图1中序遍历结果:1 2 3 4 6 7 8 9 12 22
  • 如图1前序遍历结果:6 2 1 3 4 9 8 7 12 22
  • 如图1后序遍历结果:1 4 3 2 7 8 22 12 9 6
中序遍历原理

先处理左节点,接着处理中节点最后处理右节点,递归遍历所有节点。
实现:在BinaryTree类添加中序遍历方法,callback为处理函数,主要是输出节点值(几种遍历算法涉及的callback都是统一个方法),在调用几种遍历算法作为参数转入。

// 中序遍历二叉树
var traverseNodesLDR = function(node,callback){
 if(node !== null){
    traverseNodesLDR(node.left,callback);
    callback(node.key);
    traverseNodesLDR(node.right,callback)
 }
}
// 中序遍历算法
this.LDR = function(callback){
   traverseNodesLDR(root,callback);
}

前序遍历原理

先处理中节点,接着处理左节点再处理右节点,递归遍历所有所有节点。
实现:在BinaryTree类中添加前序遍历方法

// 前序遍历二叉树
var traverseNodesDLR = function(node,callback){
	if(node!==null){
		callback(node.key);
		traverseNodesDLR(node.left,callback);
		traverseNodesDLR(node.right,callback);
	}
}
//前序遍历算法
this.DLR = function(callback) {
	traverseNodesDLR(root,callback);
}

后序遍历原理

先处理左点击(右节点),接着处理右节点(左节点)最后处理中节点
实现:在BinaryTree中添加后序遍历方法

//后序遍历二叉树
var traverseNodesLRD = function(node,callback){
   if(node!==null){
   	traverseNodesLRD(node.left,callback);
   	traverseNodesLRD(node.right,callback);
   	callback(node.key);
   }
}
//后序遍历算法
this.LRD = function(callback){
	traverseNodesLRD(root,callback);
}

二叉树查找

首先需要理解什么是叶子:叶子即是没有左右孩子的节点,如图1叶子有 1 4 7 22;
叶子的父节点即是叶子节点上一层节点,如图1叶子父节点有2 3 8 12

查找最大值算法实现

原理:查找右子树(针对本案例)的右叶子或中节点(右叶子的父节点)

//遍历右子树
var traverseRight = function(node,callback){
	if(node!==null){
		if(node.right!==null){
           traverseRight(node.right,callback);
		}else{
			callback(node.key);
		}
	}else{
     throw new TypeError('空二叉树')
	}
}
//获取最大值
this.getMaxValue = function(callback){
	traverseRight(root,callback);
}

查找最小值算法实现

原理:在左子树中查找左叶子节点或中节点(左叶子的父节点)

//遍历左子树
var traverseLeft = function(node,callback){
	if(node!=null){

    	// 排序二叉树(本demo约定)右子树大于左子树
        if(node.left!==null){
        	traverseLeft(node.left,callback);
        }else
        {
        	// 返回最小值
        	callback(node.key)
        }
	} else{
     throw new TypeError('空二叉树')
	}
}
//获取最小值
this.getMinValue=function(callback){
	traverseLeft(root,callback);
}

判断一个值是否在二叉树中算法实现

原理:判断给定的值是否大于根节点,大于递归遍历右子树(本案例),小于小于遍历左子树,存在则返回true,否则false

// 遍历查找二叉树
var traverseNode= function(node,key){
	if(node === null){
		return false;
	}

	if(node.key<key){
		return traverseNode(node.right,key);
	}else if(node.key>key){
		return traverseNode(node.left,key)
	}else{
		return true;
	}
}
//查找指定的值是否在二叉树中
this.isExitInTree = function(key){
	return traverseNode(root,key);
}

节点删除算法实现

原理:

  1. 删除叶子节点(左右孩子节点都没有),直接把叶子节点设置为null接口
  2. 删除仅包含左子节点的中节点,把左孩子节点指向null
  3. 删除仅包含右节点的中节点,把有孩子节点设置为null
  4. 删除左右节点都有的中间节点,查找右子树的最小节点值,替换删除节点值,并把右子树最小的节点值删除
//删除节点
var removeNode = function(node,key){
  if(node ===null){
  	return null
  }
  if(key<node.key){
  	node.left=removeNode(node.left,key);
  	return node;
  }else if(key>node.key){
  	node.right = removeNode(node.right,key);
  	return node;
  }else{
  	//删除节点
  	//1、删除没有左右子树的节点
  	if(node.left ===null && node.right ===null){
  		node =null;
  		return node;
  	}
  	//2、删除只有右子树的节点
  	if(node.left === null){
  		node = node.right;
  		return node;
  	}
  	//3、删除只有左子树的节点
  	if(node.right === null){
  		node = node.left;
  		return node;
  	}
  	//4、删除左右子树都有的节点

  	//4.1查找右子树中最小的节点N,
    var minNode = getMinNode(node.right);
    //4.2用N替换需要删除的节点,
    node.key = minNode.key;
    //4.3删除右子树最小的节点
    node.right = removeNode(node.right,minNode.key);
    return node
  }
}

this.deleteNode = function(key){
   removeNode(root,key);
}

完整算法代码

//二叉树节点
function BinaryTree() {
    
    // 节点结构
	var Node = function(key){
		this.key = key;
		this.left = null;
		this.right = null;
	};

    //根节点
	var root = null;

    //新增节点
    var insertNode = function(node,newNode){
    	//约定右孩子都大于左孩子节点
    	if(newNode.key<node.key){
    		if(node.left === null){//没有左孩子,则新增左孩子
    			node.left = newNode;
    		}else{
    			//如果有左孩子则递归算法实现插入左孩子节点
    			insertNode(node.left,newNode);
    		}
    	}else {
    		//如果有孩子为null,则新增右孩子
    		if(node.right===null){
    			node.right = newNode;
    		}else{
    			//如果有左孩子则递归算法实现插入右孩子节点
    			insertNode(node.right,newNode);
    		}
    	}
    };
    // 插入新节点
	this.insert =function(key){
		//创建节点
       var node = new Node(key);
       if(root === null){//判断是否为根节点
       	 root = node;
       }else {
       	// 不是根节点则新增节点
       	 insertNode(root,node);
       }
	};

   // 中序遍历二叉树
    var traverseNodesLDR = function(node,callback){
     if(node !== null){
        traverseNodesLDR(node.left,callback);
        callback(node.key);
        traverseNodesLDR(node.right,callback)
     }
    }
    // 中序遍历算法
	this.LDR = function(callback){
       traverseNodesLDR(root,callback);
	}

    // 前序遍历二叉树
	var traverseNodesDLR = function(node,callback){
		if(node!==null){
			callback(node.key);
			traverseNodesDLR(node.left,callback);
			traverseNodesDLR(node.right,callback);
		}
	}
	//前序遍历算法
	this.DLR = function(callback) {
		traverseNodesDLR(root,callback);
	}

    //后序遍历二叉树
    var traverseNodesLRD = function(node,callback){
       if(node!==null){
       	traverseNodesLRD(node.left,callback);
       	traverseNodesLRD(node.right,callback);
       	callback(node.key);
       }
    }
    //后序遍历算法
	this.LRD = function(callback){
		traverseNodesLRD(root,callback);
	}

    //===================二叉树查找======================== 
    //遍历左子树
    var traverseLeft = function(node,callback){
    	if(node!=null){

	    	// 排序二叉树(本demo约定)右子树大于左子树
	        if(node.left!==null){
	        	traverseLeft(node.left,callback);
	        }else
	        {
	        	// 返回最小值
	        	callback(node.key)
	        }
    	} else{
         throw new TypeError('空二叉树')
    	}
    }
	//获取最小值
	this.getMinValue=function(callback){
		traverseLeft(root,callback);
	}
    
    //遍历右子树
    var traverseRight = function(node,callback){
    	if(node!==null){
    		if(node.right!==null){
               traverseRight(node.right,callback);
    		}else{
    			callback(node.key);
    		}
    	}else{
         throw new TypeError('空二叉树')
    	}
    }
    //获取最大值
	this.getMaxValue = function(callback){
		traverseRight(root,callback);
	}

    // 遍历查找二叉树
    var traverseNode= function(node,key){
    	if(node === null){
    		return false;
    	}

		if(node.key<key){
			return traverseNode(node.right,key);
		}else if(node.key>key){
			return traverseNode(node.left,key)
		}else{
			return true;
		}
    }
    //查找指定的值是否在二叉树中
	this.isExitInTree = function(key){
		return traverseNode(root,key);
	}

     //查询最小节点
     var getMinNode = function(node){
     	if(node!==null){
     		if(node.left !== null){
     			return getMinNode(node.left)
     		}else{
     			return node;
     		}
     	}
     }
     
	//删除节点
	var removeNode = function(node,key){
      if(node ===null){
      	return null
      }
      if(key<node.key){
      	node.left=removeNode(node.left,key);
      	return node;
      }else if(key>node.key){
      	node.right = removeNode(node.right,key);
      	return node;
      }else{
      	//删除节点
      	//1、删除没有左右子树的节点
      	if(node.left ===null && node.right ===null){
      		node =null;
      		return node;
      	}
      	//2、删除只有右子树的节点
      	if(node.left === null){
      		node = node.right;
      		return node;
      	}
      	//3、删除只有左子树的节点
      	if(node.right === null){
      		node = node.left;
      		return node;
      	}
      	//4、删除左右子树都有的节点

      	//4.1查找右子树中最小的节点N,
        var minNode = getMinNode(node.right);
        //4.2用N替换需要删除的节点,
        node.key = minNode.key;
        //4.3删除右子树最小的节点
        node.right = removeNode(node.right,minNode.key);
        return node
      }
	}
 
   this.deleteNode = function(key){
   	removeNode(root,key);
   }

}


//构建排序二叉树 
var nodes = [6,2,3,4,9,8,7,12,1,22]
var binaryTree = new BinaryTree();
nodes.forEach(function(key){
    binaryTree.insert(key);
});

//处理节点方法
var callback = function(key){
	console.log(key)
}
//中序遍历
console.log('中序遍历结果:')
binaryTree.LDR(callback)

//前序遍历
console.log('前序遍历结果:');
binaryTree.DLR(callback);

//后序遍历
console.log('后序遍历结果:');
binaryTree.LRD(callback);

console.log("最大值:");
binaryTree.getMaxValue(callback);

console.log("最小值:");
binaryTree.getMinValue(callback);

console.log('22是否存在二叉树中:'+binaryTree.isExitInTree(22))

console.log('删除节点')
binaryTree.deleteNode(4);
binaryTree.deleteNode(9);

更多分享请关注

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值