javascript数据结构之树(二叉搜索树,平衡二叉树,红黑树)

256 阅读1分钟

一种非顺序数据结构--树。

树是一种分层数据的抽象模型,一个树结构包含一系列存在父子关系的节点,每个节点都有一个父节点(除了顶部第一个节点)以及零个或多个子节点,位于树顶部的节点叫根节点。

二叉树和二叉搜索树

二叉树中的节点最多只能有两个节点,一个左侧的节点,一个右侧节点。
二叉搜索树(BST)是二叉树的一种,但是只允许在左侧储存比父节点小的数据,在右侧储存比父节点大的数据。

和链表一样,通过引用来表示节点之间的关系,在双向链表里,每个节点有俩引用,一个指向上一个节点,一个指向下一个节点,对于二叉搜索树也使用同样方式,不同的地方是一个指向左侧节点,一个指向右侧节点(树中会称节点为键)。

  1. insert(key) 树里插入键
  2. search(key) 树里查找一个键
  3. inOrderTraverse() 中序遍历所有(上行顺序遍历,先左,自身,右)
  4. preOrderTraverse() 先序遍历所有(先自身,左,右)
  5. postOrderTraverse() 后序遍历所有(先左,右,自身)
  6. min() 获取树最小值
  7. max() 获取树最大值
  8. remove(key) 删除树里某个键

中序,先序,后序,都是递归实现,描述可能不太具体,具体还是看代码把。

const Compare = {
    LESS_THAN:-1,
    BIGGER_THAN:1
}
function defultCompare(a,b){
    if(a===b){
        return 0;
    }
    return a<b?Compare.LESS_THAN:Compare.BIGGER_THAN;
}

class Node{
    constructor(key){
        this.key = key;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree{
    constructor(compareFn = defaultCompare){
        this.compareFn = compareFn;
        this.root = null;
    }
    insert(key){
        if(!this.root){
            this.root = new Node(key);
        }else{
            this.insertNode(this.root,key);
        }
    }
    insertNode(node,key){//查找对比键大小插入,最后生成结构如上图
        if(this.compareFn(key,node.key)===Compare.LESS_THAN){
            if(!node.left){
                node.left = new Node(key);
            }else{
                this.insertNode(node.left,key);
            }
        } else {
            if(!node.right){
                node.right = new Node(key);
            }else{
                this.insertNode(node.right,key);
            }
        }
    }
    search(key){
        return this.searachNode(this.root,key);
    }
    searachNode(node,key){
        if(!node){
            return false;
        }
        if(this.compareFn(key,node.key)===Compare.LESS_THAN){
            return this.searachNode(node.left,key);
        }else if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
            return this.searachNode(node.right,key);
        }else{
            return true;//可以根据自己需要返回
        }
    }
    min(){
        return this.minNode(this.root);
    }
    minNode(node){
       let current = node;
       while(current&&current.left){
            current = current.left;
       }
       return current;
    }
    max(){
        return this.maxNode(this.root);
    }
    maxNode(node){
       let current = node;
       while(current&&current.right){
            current = current.right;
       }
       return current;
    }
    remove(key){
        this.root = this.removeNode(this.root,key)
    }
    removeNode(node,key){
        if(!node){
            return null;
        }
        if(this.compareFn(key,node.key)===Compare.LESS_THAN){
        //向左查找
            node.left = this.removeNode(node.left,key);
            return node;
        }else if(this.compareFn(key,node.key)===Compare.BIGGER_THAN){
        //向右查找
            node.right = this.removeNode(node.right,key);
            return node;
        }else{//找到了
            if(!node.left&&!node.right){
                node = null;
                return node;
            }
            if(!node.left){
                node = node.right;
                return node;
            }else if(!node.right){
                node = node.left;
                return node;
            }
            //第三种情况,移除的节点有两个子节点,先找到要移除节点右边子树里最小的节点,然后移到当前删除位置,然后这会会有两个相同的键(当前删除位置===右侧子树最小节点),所以把右侧子树最小节点删掉。
            const aux = this.minNode(node.right);
            node.key = aux.key;
            node.right = this.removeNode(node.right,aux.key);
            return node;
        }
    }
    inOrderTraverse(cb){
        this.inOrderTraverseNode(this.root,cb);
    }
    inOrderTraverseNode(node,cb){
        if(node){
            this.inOrderTraverseNode(node.left,cb);
            cb(node.key);
            this.inOrderTraverseNode(node.right,cb);
        }
    }
    preOrderTraverse(cb){
        this.preOrderTraverseNode(this.root,cb)
    }
    preOrderTraverseNode(node,cb){
        if(node){
            cb(node.key);
            this.preOrderTraverseNode(node.left,cb);
            this.preOrderTraverseNode(node.right,cb);
        }
    }
    postOrderTraverse(cb){
        this.postOrderTraverseNode(this.root,cb);
    }
    postOrderTraverseNode(node,cb){
        if(node){
            this.postOrderTraverseNode(node.left,cb);
            this.postOrderTraverseNode(node.right,cb);
            cb(node.key);
        }
    }
}

const printNode = (value)=>console.log(value);//可以用此做回掉测试

未完待续,明天继续补充
做个笔记,欢迎指出错误,该内容借鉴与学习javascript数据结构与算法