有根树
树是一种数据结构,由结点和连接结点的边组成。
在本图中,结点A就是根结点,结点A是结点B的父亲结点,结点E与结点K是结点B的兄弟结点,结点C与结点D是结点B的子结点。唯有根结点没有父亲结点,没有子结点的结点为叶子结点。
有根树的中结点的子结点个数表示这个节点的度,例如A的度就是3,B的度就是2。
从根到节点的路径长度成为节点的深度,节点x到叶节点的最大路径长度称之为节点的高。一棵树中根节点的高度最大,也就是树的高度。例如G的高度是1,树高3。
二叉树
如果一棵树拥有一个根节点,且所有节点的子结点个数都不超过2,那么这棵树就称之为二叉树。
二叉搜索树是二叉树的一种,只允许在左侧节点存储比父节点小的值,在右侧节点存储比父节点大的值,如下图:
创建二叉树
二叉树的数据结构组织形式
function BinarySearchTree(){
var Node = function(key){//定义节点结构
this.key = key;
this.left = null;
this.right = null;
}
var root = null;//初始化根节点
}
实现方法
inser():插入一个新的键值
search():查找一个键值,如果找不到返回false,找到返回true
inOrderTraverse:中序遍历节点
preOrderTraverse:先序遍历节点
postOrderTraverse:后序遍历节点
minNode:返回最小值;
maxNode:返回最大值;
remove():删除一个元素
插入
this.insert = function(key){//在树中插入一个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.key < newNode.key){
if(node.right === null){
node.right = newNode;
}else{
insertNode(node.right,newNode);
}
}
}
方法使用
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);
遍历
中序遍历
this.inOrderTraverse = function(callback){//中序遍历(LDR)是 二叉树遍历的一种,也叫做 中根遍历、中序周游。在二叉树中,先左后根再右。巧记:左根右。
inOrderTraverseNode(root,callback);
}
var inOrderTraverseNode = function(node,callback){
if(node !== null){
inOrderTraverseNode(node.left,callback);
callback(node.key);
inOrderTraverseNode(node.right,callback);
}
}
方法使用
function printNode(value){
console.log(value);
}
tree.inOrderTraverse(printNode);
控制台输出
先序遍历
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);
}
}
方法使用
tree.preOrderTraverse(printNode);
控制台输出
后序遍历
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);
}
}
方法使用
tree.preOrderTraverse(printNode);
控制台输出
搜索
查找最小值
var findMin = function(node){
if(node){
while(node && node.left !== null){
node = node.left;
}
return node.key;
// if(node.left !== null){此方法费时费力
// return findMin(node.left);
// }else{
// return node.key;
// }
}
return null;
}
方法使用
console.log(tree.minNode());//3
查找最大值
var findMax = function(node){
if(node){
while(node && node.right !== null){
node = node.right;
}
return node.key;
// if(node.right !== null){//同上
// return findMax(node.right);
// }else{
// return node.key;
// }
}
return null;
}
方法使用
console.log(tree.maxNode());//25
查找特定值
this.search = function(key){//查找特定值
return searchNode(root,key);
}
var searchNode = function(node,key){
if(node !== null){
if(key < node.key){
return searchNode(node.left,key);
}else if(key > node.key){
return searchNode(node.right,key);
}else{
return true;
}
}
return false;
}
方法使用
console.log(tree.search(25));//true
console.log(tree.search(-6));//false
console.log(tree.remove(7));//如果成功,返回的总是根
删除
(1). 如果要删除的节点是叶子节点,其没有任何子树,那么就直接删除之;
(2). 如果要删除的节点仅有左子树或者仅有右子树,那么就将其对应的左子树或者右子树这个非空子树整个移动到删除节点的位置,以完成独子承父业;
(3). 如果要删除的节点其左右子树都有节点,具体的做法可以选择:
a. 要么沿着要删除节点的左子树,一直向右走,找出最右的节点,取出来替换该删除节点;
b. 或者沿着删除节点的右子树,一直向左走,找出最左的节点,然后替换之
this.remove = function(key){
return removeNode(root,key);
}
var removeNode = function(node,key){
/*
*(1). 如果要删除的节点是叶子节点,其没有任何子树,那么就直接删除之;
*(2). 如果要删除的节点仅有左子树或者仅有右子树,那么就将其对应的左子树或者右子树这个非空子树整个移动到删除节点的位置,以完成独子承父业;
*(3). 如果要删除的节点其左右子树都有节点,具体的做法可以选择:
*a. 要么沿着要删除节点的左子树,一直向右走,找出最右的节点,取出来替换该删除节点;
*b. 或者沿着删除节点的右子树,一直向左走,找出最左的节点,然后替换之
*/
if(node !== null){//判断是否存在节点
if(node.key > key){
removeNode(node.left,key);
return node;
}else if(node.key <key){
removeNode(node.right,key);
return node;
}else{
if(node.left === null && node.right === null){//删除叶子节点
node = null;
return node;
}
/*删除只有右节点的节点*/
if(node.left === null){
node = node.right;
return node;
}
/*删除只有左节点的节点*/
if(node.right === null){
node = node.left;
return node;
}
//双节点的删除
var suitNode = findMax(node);
node.key = suitNode.key;
node.left = removeNode(node.left,suitNode.key);
return node;
}
}else{
return null;
}
}
方法使用
console.log(tree.remove(7));//如果成功,返回的总是根
删除过程
叶子节点:
一个节点:
两个节点: