二叉搜索树(BST)

645 阅读3分钟

二叉树什么样

1625037679.jpg

通俗理解

1.一颗树只有一个根节点,这颗树根节点为7。

2.一个圈为一个节点,一个节点,可以有或者没有左右子节点,比如7,左节点为4,右节点为9。

3.一个节点有父节点,父节点可为null,比如7的父节点为null,4的父节点为7。

4.一个节点同时有左子节点和右子节点,那么这个节点的度为2;一个节点有左子节点(右子节点)而没有右子节点(左子节点),即只有一个子节点,这个节点的度为1;一个节点没有子节点,即度为0,同时称这个节点为叶子节点。

5.一颗树总有节点为n个,度为1的节点有n1个,度为节点有n2个,叶子节点有n0个,那么n = n2+n1+n0。

6.推导公式:叶子节点n0 = n2+1。

7.所有的树都可遍历,遍历方法有前序遍历,后序遍历,中序遍历,层序遍历。

8.一个节点有前驱节点和后续节点,也就是这棵树按照中序遍历出来的数据(正常情况,数字排序是从小到大),比如7这个节点的前驱节点是4,后续节点为9。

生成节点对象

node.js

//根据特点,生成节点对象

class Node {
	constructor(val,parent) {
		this.element = val ? val : null;
		this.left=null;
		this.right=null;
		this.parent = parent ? parent : null;
	}
	
	hasTwoChildren(){
		if(this.left !=null && this.right!=null){
			return true;
		}else{
			return false;
		}
	}
	
}

生成普遍树的对象


bstCommon.js

//根据普遍二叉树生成二叉树的对象

document.write("<script language=javascript src='./queue.js'></script>");
// 创建一个普通树的类,归纳类的属性和方法

class BSTCommon {
	constructor(arg) {
		this.size=0;
		
	}
	
	//树是否为空
	isEmpty(){
		
		return this.size;
	}
	
	//清空树
	clear(){
		this.size=0;
	}
	
	//前序遍历(根节点->左节点->右节点)
     beforeInOrder(node){
		if(!(node==null)){
			console.log(node.element)
			this.beforeInOrder(node.left)
			this.beforeInOrder(node.right)
		}
	}
	
	
	
	//后序遍历(左节点->右节点->根节点)
	behindInOrder(node){
		if(!(node==null)){
			this.beforeInOrder(node.left)
			this.beforeInOrder(node.right)
			console.log(node.element)
		}
	}
	
	//中序遍历(左节点->根节点->右节点)
	middleInOrder(node){
		if(!(node==null)){
			this.beforeInOrder(node.left)
			console.log(node.element)
			this.beforeInOrder(node.right)
		}
	}
	
	//层序遍历
	levelInOrder(node){
		if(node==null) return;
		var queue = new Queue();
		queue.push(node)
		while(!queue.isEmpty()){
			var node = queue.pop();
			console.log(node)
			if(node.left!=null){
				queue.push(node.left)
			}
			if(node.right!=null){
				queue.push(node.right)
			}
		}
	}
	
	
	//返回二叉树的前驱节点
	beforeNode(node){
		if(node ==null) return null;
		// 第一种情况,前驱在左子树,即node.left!=null
		if(node.left!=null){
			var p = node.left;
			while(p.right!=null){
				p = p.right;
			}
			return p;
		}
		// 第二种情况,右子树找前驱节点的情况,即node.left==null,只能往上面找,直到找节点是在父节点的right上
		while(node.parent !=null && node == node.parent.right){
			node = node.parent;
		}
		//此时从上面退出循环,就是parent为空了或者node==node.parent.right
		return node.parent
	}
	
        
        
	//返回二叉树的后续节点
	beHideNode(node){
		if(node ==null) return null;
		// 第一种情况,前驱在左子树,即node.left!=null
		if(node.right!=null){
			var p = node.right;
			while(p.left!=null){
				p = p.left;
			}
			return p;
		}
		// 第二种情况,右子树找前驱节点的情况,即node.left==null,只能往上面找,直到找节点是在父节点的right上
		while(node.parent !=null && node == node.parent.left){
			node = node.parent;
		}
		//此时从上面退出循环,就是parent为空了或者node==node.parent.right
		return node.parent
	}
}

queue.js

//队列对象
class Queue {
	constructor(arg) {
		this.dataStore = [];
		
	}
	
	//入队
	push(val){
		this.dataStore.push(val)
	}
	
	//出队
	pop(){
		return this.dataStore.shift()
	}
	
	isEmpty(){
		if(this.dataStore.length==0){
			return true;
		}else{
			return false;
		}
	}
}



二叉搜索树的特点

  1. 任意一个节点的值都大于其左子树所有节点的值

  2. 任意一个节点的值都小于其右子树所有节点的值

根据二叉搜索树生成对象,这个对象继承于普遍树对象


bts.js

document.write("<script language=javascript src='./node.js'></script>");

class BST extends BSTCommon {
	constructor(arg) {
		super()
		this.root=new Node();
	}
	
	
	/*
	参数不一定是数值,可能是对象,这就需要个人定义比较规则,方便参数传入进行比较
	*/
	add(data){  //关键点在于parent
		if(this.root==null) return this.root = new Node(data);
		var node = this.root;
		var parent;
		while(true){
			parent = node;
			if(data<node.element){
				node= node.left;
				if(node==null){
					parent.left = new Node(data,parent)
					break;
				}
			}else{
				node= node.right;
				if(node==null){
					parent.right = new Node(data,parent)
					break;
				}
			}
		}
		this.size++
	}
	
	/*根据删除的值找到当前的删除的节点对象*/
	remove(data){
		this.removeNode(this.findNode(data))
		// console.log('寻找到的node')
		// console.log(this.findNode(data))
	}
	
	//寻找值所在的node对象
	findNode(data){
		if(this.root==null) return null;
		var node = this.root;
		while(node !=null && node.element!= data){
			if(data<node.element){
				node= node.left;
			}else{
				node= node.right;
			}
		}
		return node;
	}
	
	//真正删除操作
	removeNode(node){
		if(node==null) return;
		this.size--;
		//判断度为2的节点,即节点左节点和右节点都不为空
		if(node.hasTwoChildren()){
			var succeedNode = this.beHideNode(node);  //找到node的后续节点
			node.element = succeedNode.element;
			node = succeedNode;  //暂时不删除,等到后面,统一处理
		}
		//此时到这里的node的节点肯定是度为1或者度为0,度为1时,不知是在左边还是右边,先拿值
		var replacement = node.left != null ? node.left : node.right;
		if(replacement!=null){  //这里是度为1的节点,因为已经从node的左边或者右边那里取到值
			
			//更改replacement的parent指向
			replacement.parent = node.parent;
			//然后更新parent的left和right 的指向
			// 得判断parent是否为null
			if(node.parent ==null){
				this.root = replacement;
			}else{
				if(node == node.parent.left){
					node.parent.left = replacement
				}else if(node == node.parent.right){
					node.parent.right = replacement
				}
			}
	
		}else{  //这里是度为0的节点,但是不知道是不是根节点
			if(node.parent == null){  //根节点
				this.root=null;
			}else{  //不是根节点
				if(node == node.parent.left){
					node.parent.left=null
				}else{
					node.parent.right=null
				}
			}
		}
		
		
	}
	
        
        //包含某个节点
	contain(data){
		if(this.root==null) return false;
		var node = this.root;
		while(node !=null && node.element!= data){
			if(data<node.element){
				node= node.left;
			}else{
				node= node.right;
			}
		}
		//退出循环时,此时node可能是空,即null,可能是node的element已经等于data了
		return Boolean(node);
	}
}

对象生成后测试运行

1625033093(1).jpg

测试在二叉树.html:

//二叉树.html

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
	</body>
</html>
<script type="text/javascript" src="./bstCommon.js"></script>
<script type="text/javascript" src="./bst.js"></script>
<script type="text/javascript">
	var bst = new BST()
	var arr = [4,2,8,6,9,1,5]
	for(var i=0;i<arr.length;i++){
		bst.add(arr[i])
	}
	// console.log('前序遍历')
	// bst.beforeInOrder(bst.root)
	// console.log('后序遍历')
	// bst.behindInOrder(bst.root)
	// console.log('中序遍历')
	// bst.middleInOrder(bst.root)
	console.log('层序遍历')
	bst.levelInOrder(bst.root)
	// console.log(bst.contain(3))
	bst.remove(6)
	console.log('层序遍历')
	bst.levelInOrder(bst.root)
	
</script>

总结

这是基于js代码生成的简单的二叉搜索树的接口,而对象里面可以添加很多属性和方法,这里只写了很简单的方法,增加、删除、遍历,这几个方法只是方便了解这个二叉树的知识,而基于二叉搜索树的基础上,还有很多的AVL树,B树,红黑树,这些树可以继承二叉搜索树的对象。