js二叉排序树

/*
    如果我们要查找的集合本身没有顺序,在频繁查找的同时还经常插入和删除,构建一棵二叉树比较合适。
 */

//  定义节点
function Node(data,left,right,parent){
    this.data=data;     // 结点存放的值
    this.parent=parent; // 父结点
    this.left=left;     // 左子结点
    this.right=right;   // 右子结点
}

//  定义二叉排序树(BST)类
function BST() {
    this.root=null;           // 根结点
    this.insert=insert;       // 插入
    this.insert2AVL=insert2AVL; // 插入,并调整为平衡二叉树
    this.remove=remove;       // remove和cutoff都是删除一个结点,实现方法不同
    this.cutoff=cutoff;
    this.preOrderTraverse=preOrderTraverse; // 前序遍历
    this.inOrderTraverse=inOrderTraverse;   // 中序遍历,结果是升序的
    this.postOrderTraverse=preOrderTraverse;// 后序遍历
    this.getMin=getMin; // 查找最小值
    this.getMax=getMax; // 查找最大值
    this.find=find;     // 查找指定值
}

/*
    插入

 */
function insert(data) {
    var node=new Node(data,null,null,null);
    if(this.root===null){   // 如果是一棵空树,直接 this.root=node
        this.root=node;
    }else{  // 如果不是空树
        var cur=this.root; // 定义一个遍历指针,指向当前node,初始值为根节点
        var parent; // 定义一个指针,指向当前node的父节点
        while(true){
            parent=cur;
            if(data===cur.data){    // 如果插入的值已经存在,则终止插入
                break;
            }
            if(data<cur.data){  //如果插入的值小于当前节点的值,遍历指针指向其left node
                cur=cur.left;
                if(!cur){   // 如果left node不存在,则此处(parent.left)就是目标位置。
                    parent.left=node;
                    node.parent=parent;
                    break;
                }
            }else{
                cur=cur.right;
                if(!cur){
                    parent.right=node;
                    node.parent=parent;
                    break;
                }
            }
        }
    }
}

/*
    插入,并调整为平衡二叉树

 */
function insert2AVL() {

}

/*
    遍历。

 */
//中序遍历,结果以数组形式返回
function inOrderTraverse() {
    var arr=[];
    inOrder(this.root,arr);
    return arr;
}

function inOrder(BST,arr) {
    if(BST){
        inOrder(BST.left,arr);
        arr.push(BST.data);
        inOrder(BST.right,arr);
    }
}

//前遍历,结果以数组形式返回
function preOrderTraverse() {
    var arr=[];
    preOrder(this.root,arr);
    return arr;
}
function preOrder(BST,arr) { //将BST前序输出到arr数组里
    if(BST){
        arr.push(BST.data);
        preOrder(BST.left,arr);
        preOrder(BST.right,arr);
    }
}

//后序遍历,结果以数组形式返回
function postOrderTraverse() {
    var arr=[];
    postOrder(this.root,arr);
    return arr;
}
function postOrder(BST,arr) { //将BST后序输出到arr数组里
    if(BST){
        postOrder(BST.left,arr);
        postOrder(BST.right,arr);
        arr.push(BST.data);
    }
}

/*
    查找(最大值、最小值、特定值)

 */

function getMin() {
    var cur=this.root;
    while (cur.left){
        cur=cur.left;
    }
    return cur.data;
}

function getMax() {
    var cur=this.root;
    while (cur.right){
        cur=cur.right;
    }
    return cur.data;
}

function find(item) {
    var cur=this.root;
    while (cur){
        if(cur.data===item){return true;}
        if(cur.data>item){cur=cur.left;}
        else {cur=cur.right;}
    }
    return false;
}

/*
    删除方法1:不需要知道父结点

 */
function remove(data) {
    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=node.right;
        while (tempNode.left){ // 找到直接后继结点,也就是右子树的最小结点
            tempNode=tempNode.left;
        }

        node.data = tempNode.data;  // 将直接后继节点的值
        node.right = removeNode(node.right, tempNode.data); // node的右子树等于去除直接后继结点后的子树
        return node;
    } else if (data < node.data) {
        node.left = removeNode(node.left, data);
        return node;
    } else {
        node.right = removeNode(node.right, data);
        return node;
    }
}


/*
    删除方法2:结点需要知道父结点

 */
function cutoff(data) {
    cut(this.root,data);
}
function cut(BST,item) {
    if(!BST){return false;}
    else{
        if(item===BST.data){
            return cutNode(BST);
        }else if(item<BST.data){
            return cut(BST.left,item);
        }else{
            return cut(BST.right,item);
        }
    }
}
// 从二叉排序树中删除结点node,并重接它的左或右子树
function cutNode(node) {
    var q,s;
    if(node.left===null&&node.right===null){ // 删除节点为叶子结点,直接设置为null,浏览器在空闲时垃圾回收
        if(node===node.parent.left){node.parent.left=null;}
        if(node===node.parent.right){node.parent.right=null;}
    }else if(node.left===null){ // 删除结点只有右子树
        /*q=node;
        node=node.right; // 原结点并没有改变
        q=null;*/
        // 注意! 必能通过直接修改node来达到目的,要通过修改node里面的内容。
        q=node.right; // 先把node的右子树保存下来
        node.data=q.data;
        node.left=q.left;
        node.right=q.right;
        q=null;
    }else if(node.right===null){ // 删除结点只有左子树
        q=node.left; // 先把node的左子树保存下来
        node.data=q.data;
        node.left=q.left;
        node.right=q.right;
        q=null;
    }else{   // 删除结点左右子树都有
        q=node; s=node.left;
        while(s.right){ // 转左,再向右找到尽头(待删结点的前驱s)
            q=s; s=s.right;
        }
        node.data=s.data; // 将直接前驱的值赋给待删结点
        if(q!==node){
            q.right=s.left; // 重接q的右子树
        }else{
            q.left=s.left; // 重接q的左子树
        }
        s=null;
    }
    return true;
}

var arr=[62,88,58,47,35,73,51,99,37,93];
var bst=new BST();
arr.forEach(function (item) {
    bst.insert(item);
});

console.log(bst.preOrderTraverse()); //[ 62, 58, 47, 35, 37, 51, 88, 73, 99, 93 ]
console.log(bst.inOrderTraverse()); //[ 35, 37, 47, 51, 58, 62, 73, 88, 93, 99 ]
console.log(bst.postOrderTraverse()); //[ 62, 58, 47, 35, 37, 51, 88, 73, 99, 93 ]

console.log(bst.getMin()); //35
console.log(bst.getMax()); //99
console.log(bst.find(62)); //true

bst.remove(62);
console.log(bst.inOrderTraverse()); //[ 35, 37, 47, 51, 58, 73, 88, 93, 99 ]
bst.cutoff(58);
console.log(bst.inOrderTraverse()); //[ 35, 37, 47, 51, 73, 88, 93, 99 ]

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值