前言:二叉树是只有一个根节点,每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。
二叉树的五种基本形态
- 空二叉树(无根节点)
- 只有一个根结点
- 只有左子树
- 只有右子树
- 完全二叉树
创建一棵二叉排序树
需要思考:左节点的值全部小于根节点的,右节点反之
-
创建一个初始化二叉树的构造函数,使其具有两个属性:根节点,插入方法
function init(){ this.root = null; this.insert = insert; this.show = ()=>{ console.log(this.root); } } -
创建一个节点生成的构造函数,有三个属性:当前值,左节点及右节点
function Node(data, left, right){ this.data = data; this.left = left; this.right = right; } -
插入函数的实现,在此需要用到循环对比,如果当前插入值小于根节点,就将其和根节点root的左孩子比较,1.左孩子不存在则直接填入,2.存在则将左孩子假设为根节点root ’ ,继续与根节点root ’ 比较…。实现方法如下:
function insert(data){ let newNode = new Node(data, null, null); //根不存在,直接设置为根节点 if(!this.root){ this.root = newNode; }else{ //current:循环条件,对比直至找不到左或右子节点,结束循环 let current = this.root; //parent:临时保存现在的根节点root',结束循环前将节点插入root’的左或右 let parent; while(current){ parent = current; if(data < current.data){ current = current.left; if(current === null){ parent.left = newNode; break; } }else{ current = current.right; if(current === null){ parent.right = newNode; break; } } } } } -
检验是否创建成功
let binaryTree = new init(); binaryTree.insert(3); binaryTree.insert(2); binaryTree.insert(5); binaryTree.insert(6); binaryTree.insert(3); binaryTree.insert(5);
创建二叉树如下:
遍历二叉树
分为:
- 前序遍历
- 中序遍历
- 后序遍历
注意:
- 所谓的前中后序遍历指的是根节点,例如:前序就是根在前,按照根左右的顺序;中序即左根右。
- 不论是哪一种遍历类型,其遍历方式都是一样的,都是从根节点走下去,只是打印顺序不一样
添加read遍历方法:
function init(){
this.root = null;
this.insert = insert;
this.show = ()=>{
console.log(this.root);
}
**this.read = orderMap;**
}
实现read方法:
/**
* 前、中、后序遍历
* @param {遍历类型} type
*/
function orderMap(type){
deepMap(this.root);
function deepMap(current){
if(!current){
return;
}
let arr = [
()=>console.log(current.data),
()=>current.left && deepMap(current.left),
()=>current.right && deepMap(current.right)
];
let order = [];
switch(type){
case 1: order = [0,1,2];break;
case 2: order = [1,0,2];break;
case 3: order = [1,2,0];break;
default: break;
}
order.map(item=>{
arr[item]();
})
}
}
在创建二叉树后,调用read方法,就可以实现了
binaryTree.read(1) //前序:3 2 5 3 6 5
binaryTree.read(2) //中序:2 3 3 5 5 6
binaryTree.read(3) //后序:2 3 5 6 5 3
查找树的最大最小值(最左最右)
即:最左为最小值,最右为最大值,以下为查找最小值
function findMin(current){
let min;
while(current){
min = current.data;
current = current.left;
}
return min;
}
console.log(binaryTree.findMin(binaryTree.root)) //Node 2
删除节点
被删除节点分为三种情况:
- 为叶子节点(直接删除)
- 仅有一个子节点(假设只有右节点,令node.right = node.right.right;左同理)
- 有两个子节点(从待删除节点的左子树找节点值最大的节点A,替换待删除节点,并删除节点A;从待删除节点的右子树找节点值最小的节点A,替换待删除节点,并删除节点A。)
具体实现:
先创建一个新的二叉树:
let binaryTree2 = new init();
binaryTree2.insert(10);
binaryTree2.insert(8);
binaryTree2.insert(7);
binaryTree2.insert(9);
binaryTree2.insert(14);
binaryTree2.insert(11);
binaryTree2.insert(17);
binaryTree2.insert(10);
binaryTree2.insert(12);
binaryTree2.insert(13);
binaryTree2.insert(15);
binaryTree2.insert(19);
在init函数中添加删除方法:
function init(){
this.root = null;
this.insert = insert;
this.show = ()=>{
console.log(this.root);
}
this.read = orderMap; //前中后遍历
this.findMin = findMin; //当前树的最小值节点
this.deleteNode = deleteNode; //删除某个节点
}
完成删除函数(有两个子节点的采用第二种,即用右子树最小值替换):
function deleteNode(data){
let _this = this;
del(this.root, data)
function del(node, data){
if(!node){
return null;
}
if(node.data == data){
//被删除节点没有子节点
if(node.left === null && node.right === null) {
console.log(node)
return null;
}
//只有右节点
else if(node.left === null ) {
return node.right;
}
//只有左节点
else if(node.right === null){
return node.left;
}else{
let minRightNode = _this.findMin(node.right);
node.data = minRightNode.data;
node.right = del(node.right, node.data);
return node;
}
}else if(data < node.data){
node.left = del(node.left, data);
return node;
}else{
node.right = del(node.right, data);
return node;
}
}
}
验证:
binaryTree2.deleteNode(14);
console.log('删除节点14')
binaryTree2.show()