1.什么是二叉查找树
树在计算机中,是一种非线性的数据结构,以分层的方式存储数据。所以,树被经常用来存储具有层级关系的数据,比如文件系统中的文件。本文主要是来分析一种特殊的树,二叉树。二叉树每个节点不允许超过两个,通过这个特性,我们可以写出高效的插入、删除、查找程序。二叉树中,有一种树更为特殊,叫做二叉查找树。在二叉查找树中,相对较小的值保存在做节点中,较大的值保存在又节点中。这种特性,使得我们可以实现查找效率很高的数据结构。
2.实现二叉查找树
要实现一个二叉查找树,首先我们要定义一个Node。二叉树是由不同的Node组成,但是每个Node的基本特性是不变的。每个节点,应该有一个父节点,一个左节点的指向链接,一个右节点的指向链接(指向链接表达得不是很对,欢迎指正)。
function Node(data, left, right) {
this.data = data;
this.left = left;
this.right = right;
}
二叉查找树(Binary Search Tree),可别不知道它的英文怎么写。
首先我们创建一个类BST,一个最基本的二叉查找树。什么是最基本的二叉查找树呢?只有一个父节点,这就是最基本的二叉查找树。
function BST() {
this.root = null
}
3.插入节点
接下来我们需要实现插入操作,使得这个二叉查找树丰满起来
BST.prototype = {
insert: function(data) {
let node = new Node(data, null, null);
if (this.root == null) {
this.root = node;
} else {
let currentNode = this.root;
let parentNode;
while(true) {
parentNode = currentNode;
if (data < parentNode.data) {
currentNode = currentNode.left;
if (currentNode == null) {
parentNode.left = node;
break;
}
} else {
currentNode = currentNode.right;
if (currentNode.right == null) {
parentNode.right = node;
break;
}
}
}
}
}
}
这样,我们可以给二叉查找树添加数据了
let bst = new BST()
bst.insert(20);
bst.insert(11);
bst.insert(5);
bst.insert(30);
bst.insert(10);
bst.insert(8);
bst.insert(3);
bst.insert(22);
有了数据,就不得不说二叉查找树的遍历方式。二叉查找树的遍历方式有3中,即前序遍历,中序遍历,后续遍历。那么如何来理解这三种遍历方式呢?其实用代码的方式是最容易理解的。
前序遍历
traversal: function(node) {
if(node != null) {
console.log(node.data);
this.traversal(node.left);
this.traversal(node.right);
}
}
中序遍历
traversal: function(node) {
if(node != null) {
this.traversal(node.left);
console.log(node.data);
this.traversal(node.right);
}
}
后序遍历
traversal: function(node) {
if(node != null) {
this.traversal(node.left);
this.traversal(node.right);
console.log(this.data);
}
}
(以上代码,属于BST.prototype中)
从代码上看,前中后,三种遍历方式,仅仅只是打印本节点顺序的不同而已。记住这三种顺序,就能很轻易的理解什么是前序遍历、后序遍历和中序遍历。
日常生活中,中序遍历最常见,中序遍历后,打印的顺序是从左到右,即从小到大排列。
4.查找给定值
在二叉查找树上查找定值,需要比较每个节点上的数值和给定值的大小。通过大小比较,来确定是向左遍历还是向右遍历
find: function(node, data) {
if(data == node.data) {
return 'this value in the bst';
} else if (data < node.data) {
return this.find(node.left, data);
} else {
return this.find(node.right, data);
}
return 'this value not in the bst';
}
5.从二叉树删除节点
删除节点是整个二叉树最复杂的操作,所需要分析的情况也很多。
a.删除节点没有左子树和右子树
这种情况最好解决,直接删除掉这个节点
b.删除节点只有右子树
删除这个节点,并将这个节点的右子树的父节点指向删除节点的父节点
c.删除节点只有左子树
删除这个节点,并将这个节点的左子树的父节点指向删除节点的父节点
d.删除节点既有左子树,又有右子树,操作就有点麻烦了
简单的来说,可以用3步来操作。
1)找到这个节点
2)从这个节点的左子树中找到最大的一个节点,或者从这个节点的右子树中找到最小节点,将找到的最大(小)节点的值复制给当前节点
3)删除这个最大(小)节点,此时,删除这个最大(小)节点的情况,就属于a种情况了
代码如下
removeNode: function(node, data) {
if (node == null) {
return null;
}
if(data == node.data) {
if(node.left == null && node.right == null){
return null;
} else if (node.left == null) {
return node.right;
} else if (node.right == null){
return node.left;
}
let maxNodeOfLeftLeaves = getMaxNodeOfLeftLeaves(node.left);
node.data = maxNodeOfLeftLeaves.data;
node.left = this.removeNode(node.left, node.data);
return node;
} else if (data < node.data) {
node.left = this.removeNode(node.left, data);
return node;
} else {
node.right = this.removeNode(node.right, data);
return node;
}
},
getMaxNodeOfLeftLeaves: function(node) {
//找到左边子树最大的节点
let current = node;
while (current.right) {
current = current.right;
}
return current;
}
二叉查找树其实是一个很基础的数据结构,只要理解什么是二叉查找树,就能很好的写出相关的代码实现。