树是一种分层数据的抽象模型
一个树结构包含一系列存在子父关系的节点 每个节点都有一个父节点(除了顶部的第一个节点)以及零个或多个子节点
位于树顶部的节点叫做根节点
至少有一个子节点的节点称为内部节点
没有子元素的节点称为外部节点或叶节点
子树:由节点和它的后代构成
深度:节点的深度取决于它祖先节点的数量
高度:树的高度取决于所有节点深度的最大值 根节点在第0层 根节点的子节点在第1层 以此类推
1、二叉树与二叉搜索树
二叉树中的节点最多只能有两个节点 一个左侧子节点 一个右侧子节点
二叉搜索树(BST)是二叉树的一种 但是它只允许你在左侧节点存储比父节点小的值 在右侧节点存储比父节点大或者等于父节点的值
二叉搜索树的组织方式:
function BinarySearchTree() {
var Node = function(key){ /*和链表一样,将通过
指针来表示节点之间的关系(术语称其为边)。在双向链表中,
每个节点包含两个指针,一个指向下一个节点,另一个指向上
一个节点。对于树,使用同样的方式(也使用两个指针)。但是,
一个指向左侧子节点,另一个指向右侧子节点。因此,将声明一
个Node类来表示树中的每个节点*/
this.key = key;
this.left = null;
this.right = null;
};
var root = null; //根元素
}
★方法:
① insert(key):向树中插入一个新的键。
this.insert = function (key) {
var newNode = new Node(key); /*创建用来表示
新节点的Node类实例。只需要向构造函数传递我们想用来
插入树的节点值,它的左指针和右指针的值会由构造函数
自动设置为null。*/
if (root === null) { /*要验证这个插入操作是否
为一种特殊情况。这个特殊情况就是我们要插入的节点是树
的第一个节点。如果是,就将根节点指向新节点。*/
root = newNode;
} else {
insertNode(root, newNode); /*将节点加在非
根节点的其他位置。这种情况下,需要一个私有的辅助函数*/
}
};
//调用
var tree = new BinarySearchTree();
tree.insert(num);
insertNode函数:insertNode函数会帮助我们找到新节点应该插入的正确位置。
/*如果树非空,需要找到插入新节点的位置。因此,在调用
insertNode方法时要通过参数传入树的根节点和要插入的节点。*/
var insertNode = function (node, newNode) {
if (newNode.key < node.key) { /*如果新节点的键小于当
前节点的键(现在,当前节点就是根节点)*/
if (node.left === null) { /*那么需要检查当
前节点的左侧子节点。如果它没有左侧子节点,就在
那里插入新的节点。*/
node.left = newNode;
} else {
insertNode(node.left, newNode); /*如果有左
侧子节点,需要通过递归调用insertNode方法继续找到树的下一层。*/
}
} else {
if (node.right === null) { /*如果节点的键比当
前节点的键大,同时当前节点没有右侧子节点,就在那里插入新的
节点。如果有右侧子节点,同样需要递归调用insertNode方法,
但是要用来和新节点比较的节点将会是右侧子节点。*/
node.right = newNode;
} else {
insertNode(node.right, newNode);
}
}
};
②search(key):在树中查找一个键,如果节点存在,则返回true;如果不存在,则返回
false。
③inOrderTraverse:前中后序遍历。
④search:搜索一个特定的值。
this.search = function (key) {
return searchNode(root, key); //辅助函数{1}
};
/*searchNode方法可以用来寻找一棵树或它的任意子
树中的一个特定的值。调用它的时候传入树的根节点
作为参数。在开始算法之前,先要验证作为参数传入
的node是否合法(不是null)。如果是null的话,
说明要找的键没有找到,返回false。如果传入的节点
不是null,需要继续验证。*/
var searchNode = function (node, key) {
if (node === null) { //是否合法{2}
return false;
}
if (key < node.key) {
//如果要找的键比当前的节点小{3}
return searchNode(node.left, key);
//那么继续在左侧的子树上搜索{4}
} else if (key > node.key) {
//如果要找的键比当前的节点大{5}
return searchNode(node.right, key);
//那么就从右侧子节点开始继续搜索{6}
} else {
return true;
//否则就说明要找的键和当前节点的键相等{7}
}
};
//调用
console.log(tree.search(1) ? 'Key 1 found.' : 'Key 1 not found.');
(1) 调用searchNode方法,传入根节点作为参数(行{1})。(node[root[11]])不是null
(行{2}),因此我们执行到行{3}。
(2) (key[1] < node[11])为ture(行{3}),因此来到行{4}并再次调用searchNode方法,传入(node[7], key[1])作为参数。
(3) (node[7])不是null({2}),因此继续执行行{3}。
(4) (key[1] < node[7])为ture(行{3}),因此来到行{4}并再次调用searchNode方法,传入(node[5], key[1])作为参数。
(5) (node[5])不是null(行{2}),因此继续执行行{3}。
(6) (key[1] < node[5])为ture(行{3}),因此来到行{4}并再次调用searchNode方法,传入(node[3], key[1])作为参数。
(7) (node[3])不是null(行{2}),因此来到行{3}。
(8) (key[1] < node[3])为真(行{3}),因此来到行{4}并再次调用searchNode方法,传入(null, key[1])作为参数。null被作为参数传入是因为node[3]是一个叶节点(它没有子节点,所以它的左侧子节点的值为null)。
(9) 节点(null)的值为null(行{2},这时要搜索的节点为null),因此返回false。
(10) 然后,方法调用会依次出栈,代码执行过程结束。
★遍历
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);
}
}
};
this.inOrderTraverse = function (callback) {
anode(root, callback);
};
var anode = function (node, callback) {
if (node !== null) {
//中序遍历 首先遍历左子树,然后访问根结点,最后遍历右子树。
anode(node.left, callback);
callback(node.key);
anode(node.right, callback);
/*前序遍历
callback(node.key);
anode(node.left, callback);
anode(node.right, callback); */
/*后序遍历
anode(node.left, callback);
anode(node.right, callback);
callback(node.key); */
}
};
}
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);
function printNode(value) {
console.log(value);
}
tree.inOrderTraverse(printNode); //3,5,7,8,9,11,15
二叉搜索树(BST)存在一个问题 树的一条边可能会非常深 为了解决这个问题 出现了一种自平衡二叉搜索树(AVL)