树是一种分层数据的抽象模型。
二叉树
二叉树中的节点最多只能有两个字节点:一个是左侧子节点,另一个是右侧子节点。
二叉搜索树
二叉搜索树(BST)是二叉树的一种,但是它只允许你在左侧节点存储比父节点小的值,在右侧节点存储比父节点大或者等于的值。
二叉搜索树代码实现:
function BinarySearchTree(){
var Node = function(key){
this.key = key;
this.left = null;
this.right = null;
}
var root = null;
// 向树中插入一个键
this.insert = function(key){
var newNode = new Node(key);
if(root === null){
root = newNode;
}else{
insertNode(root, newNode);
}
};
var insertNode = function(node, newNode){
if(newNode.key < node.key){
if(node.left === null){
node.left = newNode;
}else{
insertNode(node.left, newNode);
}
}else{
if(node.right === null){
node.right = newNode;
}else{
insertNode(node.right, newNode);
}
}
}
function printNode(value){
console.log(value);
}
// 中序遍历
// 中序遍历就是从小到大的顺序访问所有的节点。中序遍历的一种应用就是对树进行排序操作
this.inOrderTraverse = function(callback){
inOrderTraverseNode(root, callback);
}
var inOrderTraverseNode = function(node , callback){
if(node !== null){ //递归终止条件
inOrderTraverseNode(node.left, callback);
callback(node.key);
inOrderTraverseNode(node.right, callback);
}
}
// 先序遍历
// 先序遍历是以优先于后代节点的顺序访问每个节点的。先序遍历的一种应用是打印一个结构化的文档
this.preOrderTraverse = function(callback){
preOrderTraverseNode(root, callback);
}
var preOrderTraverseNode = function(node, callback){
if(node !== null){
callback(node.key);
preOrderTraverseNode(node.left, callback);
preOrderTraverseNode(node.right, callback);
}
}
// 后序遍历
// 后序遍历则是先访问节点的后代节点,再访问节点本身。后序遍历的一种应用是计算一个目录和它子目录中所有文件所占空间的大小
this.postOrderTraverse = function(callback){
postOrderTraverseNode(root, callback);
}
var postOrderTraverseNode = function(node, callback){
if(node !== null){
postOrderTraverseNode(node.left, callback);
postOrderTraverseNode(node.right, callback);
callback(node.key);
}
}
// 搜索最小值和最大值
// 搜索最小值
this.min = function(){
return minNode(root);
}
var minNode = function(node){
if(node){
while(node && node.left !== null){
node = node.left;
}
console.log(node.key);
return node.key
}
return null;
}
// 搜索最大值
this.max = function(){
return maxNode(root);
}
var maxNode = function(node){
if(node){
while(node && node.right !== null){
node = node.right;
}
console.log(node.key)
return node.key;
}
return null;
}
// 搜索一个特定的值
this.search = function(key){
return searchNode(root, key);
}
var searchNode = function(node, key){
if(node === null){
console.log('节点不存在!!!');
return false;
}
if(key < node.key){
return searchNode(node.left, key);
}else if(key > node.key){
return searchNode(node.right, key);
}else{
console.log('节点存在!!!');
return true;
}
};
// 移出一个节点
this.remove = function(key){
// 将要删除节点的父节点的指针置为null
root = removeNode(root, key);
}
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{ //键等于node.key
// 第一种情况------一个叶节点
if(node.left === null && node.right === null){
node = null;
return node;
}
// 第二种情况-----一个只有一个子节点的节点
if(node.left === null){
node = node.right;
return node;
}else if(node.right === null){
node = node.left;
return node;
}
// 第三种情况-----一个有两个子节点的节点
var findMinNode = function(node){
while(node && node.left !== null){
node = node.left;
}
return node;
}
var aux = findMinNode(node.right);
node.key = aux.key;
node.right = removeNode(node.right, aux.key);
return node;
}
}
}
测试:
function printNode(value){
console.log(value);
}
var tree = new BinarySearchTree()
tree.insert(11)
tree.insert(7)
tree.insert(15)
tree.insert(5)
tree.insert(3)
tree.insert(9)
tree.insert(8)
tree.insert(10)
tree.insert(13)
tree.insert(12)
tree.insert(14)
tree.insert(20)
tree.insert(18)
tree.insert(25)
tree.insert(6)
tree.inOrderTraverse(printNode); //3 5 6 7 8 9 10 11 12 13 14 15 18 20 25
tree.preOrderTraverse(printNode); //11 7 5 3 6 9 8 10 15 13 12 14 20 18 25
tree.postOrderTraverse(printNode); //3 6 5 8 10 9 7 12 14 13 18 25 20 15 11
tree.min(); //3
tree.max(); //25
tree.search(9); //节点存在
tree.remove(25);
tree.postOrderTraverse(printNode); //3 6 5 8 10 9 7 12 14 13 18 20 15 11
平衡二叉树
平衡二叉树(AVL):任何一个节点的左右两侧子树的高度之差最多为1.
AVL树是一种自平衡树。添加或者移除节点时,AVL树会尝试自平衡。任意一个节点的左子树和右子树高度最多相差1.添加或移除节点时,AVL树会尽可能尝试转换为完全树。