以前学过二叉树,是用c语言实现的,当时虽然听懂了,但是自己用c语言实现,还是有点困难的,现在学习了前端,发现二叉树还是很简单的。今天就来说一说。
二叉树是一种常用的数据结构,树也是一种非线性的数据结构,以分层的方式存储数据,树被用来存储具有层级关系的数据,比如系统中的文件,还有前端经常说到的dom树。现在我们研究的是一种特殊的树。
下面我们看看用js怎么实现二叉树,首先需要一个节点
function Node (data, left, right) {
this.data = data;
this.left = left;
this.right = right;
this.show = show;
}
这里的show是用来显示保存在节点里面的数据
function show () {
return this.data;
}
有了节点之后,我们需要有一颗二叉查找树BST(Binary Search Tree)
function BST () {
this.root = null;
this.insert = insert;
this.inOrder = inorder;
}
insert是插入数据的方法
/*插入数据的算法是
我们先新建一个节点,把当前的数据放到这个节点中去
我们先判读当前的树是否为空,如果为空,就把该节点作为根节点
如果不为空,就需要遍历BST了,找到要插入的位置,用一个变量存储当前的节点,一层层的进行遍历
进入BST后,我们要决定将节点放置到哪个位置,找到正确的插入点,就后跳出循环
算法如下
(1)设根节点为当前节点
(2)如果待插入节点保存的数据小于当前节点,就设新的当前节点为原节点的左节点,否则,执行第四步
(3)如果当前节点的左节点为null,就将新的节点插入这个位置,退出循环,反之继续执行下一次循环
(4)设新的节点为原节点的右节点
(5)如果当前节点的右节点为null,就将新的节点插入到这个位置,退出循环,反之,进行下一次循环
*/
function insert(data) {
var n = new Node(data, null, null);
if (this.root = null) {
this.root = n;
}else {
var current = this.root;
var parent;
while(true) {
parent = current;
if (data < current.data) {
currnet = current.left;
if (current == null) {
parent.left = n;
break;
}
} else {
current = current.right;
if (current == null) {
parent.right = n;
break;
}
}
}
}
现在二叉树初步成型,我们需要有能力遍历BST,这样就可以按照不同的顺序,比如按照数字的大小先后,显示节点上的数据
有三种遍历BST的方式,中序、先序和后序。中序遍历按照节点上的键值,以升序访问BST上的所有节点。
先序遍历先访问根节点,然后以同样的方式访问左子树和右子树,
后序遍历先访问叶子节点,从左子树到右子树,再到根节点。
中序遍历使用递归的方式最容易实现
这里的inOrder是中序遍历的意思。
先访问左子树,再访问根节点,最后访问右子树,倘若不理解递归,就先看一个例子,
function factorial(number) {
if (number == 1) {
return number;
} else {
return number * factorial(number - 1);
}
}
print(factorial(5));
画一幅图进行理解
5*factorial(4)
5*4*factorial(3)
5*4*3*factorial(2)
5*4*3*2*1
5*4*3*2
5*4*6
5*24
120
看了这个过程就很好理解了。
那中序遍历的代码如下
function inOrder(node) {
if (!(node == null)) {
inOrder(node.left);
putStr(node.show()+"");
inOrder(node.right);
}
}
先序遍历定义如下
function preOrder (node) {
if(!(node == null)) {
putstr(node.show()+"");
preOrder(node.left);
preOrder(node.right);
}
}
后序遍历
function postOrder(node) {
if (!(node == null)) {
postOrder(node.left);
postOrder(node.right);
putstr(node.show()+"");
}
}
对BST通常有三种类型的查找
(1)查找给定值
(2)查找最小值
(3)查找最大值
查找最大值和 最小值非常简单,因为较小的值总是在左子节点上,在BST上查找最小值,只需要遍历左子树,直到找到最后一个节点
getMin()方法查找BST上的最小值,
function getMin() {
var current = this.root;
while(!(current.left == null) {
current = current.left;
}
return current.data;
}
function getMax() {
var current = this.root;
while(!(current.right == null){
current = current.right;
}
return current.data;
}
查找给定值
function find(data) {
var current = this.root;
while(current != null) {
if (current.data == data) {
return current;
}
else if (data < current.data) {
current = current.left;
}
else {
current = current.right;
}
}
return null;
}
从二叉查找树上删除节点
从BST中删除节点的第一步是判断当前节点是否包含待删除数据,如果包含,则删除该结点,如果不包含,则比较当前节点上的数据和待删除的数据。如果待删除的数据小于当前节点的数据,则移至当前节点的左子节点进行比较,如果删除的数据大于当前节点上的数据,则移至当前节点的右子节点继续比较。
如果待删除节点是叶子节点,那么只需要将从父节点指向他的链接指向null,
如果待删除节点只包含一个子节点,那么原本指向他的节点就得做些调整,使其执行他的子节点。
如果待删除节点包含两个子节点,正确的做法有两种:要么查找待删除节点左子树上的最大值,要么查找其右子树上的最小值。这里我们选择后一种方式。
我们需要一个查找子树最小值的方法,后面会用他找到的最小值创建一个临时节点,将临时节点上的值赋值到待删除节点,然后在删除临时节点。
function remove (data) {
root = removeNode(this.root, data);
}
function removeNode(node, data) {
if (node == null) {
return null;
}
if (data == node.data) {
//没有子节点的节点
if(node.left == null && node.right == null) {
return null;
}
//没有左子节点的节点
if (node.left == null) {
return node.right;
}
//没有右子节点的节点
if (node.right == null) {
return node.left;
}
//有两个子节点的节点
var tempNode = getSmallest(node.right);
node.data = tempNode.data;
node.right = removeNode(node.right, tempNode.data);
return node;
}
else if(data < node.data) {
node.left = removeNode(node.left, data);
return node;
}
else {
node.right = removeNode(node.right, data);
return node;
}
else {
node.right = removeNode(node.right, data);
return node;
}
}