二叉树什么样
通俗理解
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;
}
}
}
二叉搜索树的特点
-
任意一个节点的值都大于其左子树所有节点的值
-
任意一个节点的值都小于其右子树所有节点的值
根据二叉搜索树生成对象,这个对象继承于普遍树对象
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);
}
}
对象生成后测试运行
测试在二叉树.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树,红黑树,这些树可以继承二叉搜索树的对象。