最大堆、索引堆、二叉搜索树的JavaScript实现

1、最大堆

//一定是一个完全二叉树
//堆元素索引从1开始,初始化时先将第一个元素赋值为0
// 堆有n个元素,则它的第一个叶子结点是第n/2+1个, 最后一个非叶子结点是第 n/2 //n个元素逐个插入到一个空堆中,算法复杂度为O(nlogn)
function Heap(){
    this.data=[0];
}
//利用堆的第n/2个元素是最后一个有子节点的元素,,从最后一个有子节点的元素开始向上对每个节点进行堆重构,复杂度为O(n)
Heap.prototype.initHeap=function(arr){
    this.data=this.data.concat(arr);
    for(var i=Math.floor((this.data.length-1)/2);i>=1;i--){
        shiftDown(this.data,i);
    }
};
//向堆中插入一个元素:插入到最后一个位置, 对插入的元素向上进行堆重构
Heap.prototype.insert=function(item){
    this.data.push(item);
    var n=this.data.length-1;
    shiftUp(this.data,n);
};

//删除堆中的最大元素:将最后一个元素放到第一个的位置,向下进行堆重构
Heap.prototype.extractMax=function(){
    if(this.data.length>1){
        var tail=this.data.pop();
        this.data[1]=tail;
        var n=1;
        //只要有左孩子就继续判断并交换
        shiftDown(this.data,n);
    }

};
//向下堆重构:从堆顶开始,比较当前元素和两个子元素,将若当前元素小于子元素中的一个,则将当前元素与较大的子元素交换,直到当前元素大于其子元素
function shiftDown(data,n){
    //循环条件: 有左孩子且左孩子和右孩子中至少有一个大于当前元素
    while(2*n<data.length&&(data[n]<data[2*n]||data[n]<data[2*n+1])){
        var temp;
        //有右孩子,且右孩子大于左孩子
        if(data[2*n]<data[2*n+1]){
            temp=data[n];
            data[n]=data[2*n+1];
            data[2*n+1]=temp;
            n=2*n+1;
        }else{            //包含右孩子比左孩子小和没有右孩子两种情况
            temp=data[n];
            data[n]=data[2*n];
            data[2*n]=temp;
            n=2*n;
        }
    }
}
//向上堆重构: 新元素跟父元素比较,新元素大则交换,一直到新元素比父元素小为止
function shiftUp(data,n){
    while(n>1&&data[n]>data[Math.floor(n/2)]){
        var m=Math.floor(n/2);
        var temp=data[n];
        data[n]=data[m];
        data[m]=temp;
        n=m;
    }
}

2、索引堆

//索引堆  用index存放索引,data存放数据     操作时比较数据,交换相应的索引,数据本身的位置不变
//可以用于实现优先队列   比如计算机进程的执行优先级、游戏中优先攻击敌人的选择  
//可以选出前n大的元素
function IndexHeap(){
    this.data=[0];
    this.index=[0];
}
//利用堆的第n/2个元素是最后一个有子节点的元素,,从最后一个有子节点的元素开始向上对每个节点进行堆重构,复杂度为O(n)
IndexHeap.prototype.initHeap=function(arr){
    this.data=this.data.concat(arr);
    //初始化索引
    for(var j=0;j<arr.length;j++){
        this.index.push(j+1);
    }
    for(var i=Math.floor((this.data.length-1)/2);i>=1;i--){
        shiftDown(this.data,i);
    }
};
IndexHeap.prototype.insert=function(item){
    var n=this.index.length;
    this.data.push(item);
    this.index.push(n);
    var nn=this.index.length-1;
    //向下重构索引堆
    shiftUp(this.index,nn);
};

//删除最大元素:将最后一个元素的索引放到第一个的位置,进行堆重构
IndexHeap.prototype.extractMax=function(){
    if(this.data.length>1){
        var res=this.data[this.index[1]];
        var tail=this.index.pop();
        this.index[1]=tail;
        var n=1;
        //向下进行堆重构
        shiftDown(this.index,n);
        return res;
    }
}
//向下堆重构:从堆顶开始,比较当前元素和两个子元素,将若当前元素小于子元素中的一个,则将当前元素与较大的子元素交换,直到当前元素大于其子元素
function shiftDown(data,n){
    //循环条件: 有左孩子且左孩子和右孩子中至少有一个大于当前元素
    while(2*n<data.length&&(data[this.index[n]]<data[this.index[2*n]]||data[this.index[n]]<data[this.index[2*n+1]])){
        var temp;
        //有右孩子且右孩子大于左孩子
        if(data[this.index[2*n]]<data[this.index[2*n+1]]){
            temp=this.index[n];    //交换索引
            this.index[n]=this.index[2*n+1];
            this.index[2*n+1]=temp;
            n=2*n+1;
        }else{            //包含右孩子比左孩子小和没有右孩子两种情况
            temp=this.index[n];
            this.index[n]=this.index[2*n];
            this.index[2*n]=temp;
            n=2*n;
        }
    }
}
//向上堆重构:新元素跟父元素比较,新元素大则交换,,一直到新元素比父元素小为止
function shiftUp(data,n){
    while(n>1&&data[this.index[n]]>data[this.index[Math.floor(n/2)]]){
        var m=Math.floor(n/2);
        var temp=index[n];
        index[n]=index[m];
        index[m]=temp;
        n=m;
    }
}

3、二叉搜索树

// 二叉搜索树  高效: 查找、插入、删除的复杂度都是logn
//是一个二叉树: 树的每个节点的值大于左孩子,小于右孩子
//不一定是完全二叉树
function BST(key,value,left,right){
    this.key=key;
    this.value=value;
    this.left=left;
    this.right=right;
    this.count=1;   //节点数量
    this.root=this;   //树的根节点
}

//插入节点
BST.prototype.insert=function(key,value){
    //向以node为根节点的搜索树中插入key value
    //返回插入节点后的搜索树的根
    function insertRoot(node,key,value){
        if(node===null){     //树为空则新建树
            return new BST(key,value,null,null);
        }
        if(key===node.key){   //若可以值已存在,则将刷新value的值
            node.value=value;
        }else if(node.key<key){      //插入的key值比当前节点的key值大,则将其插入到右子树
            node.right=insertRoot(node.right,key,value);
        }else{           //插入的key值比当前节点的key值小,则将其插入到左子树
            node.left=insertRoot(node.left,key,value);
        }
        return node;
    }
    this.root=insertRoot(this.root,key,value);
    this.count++;
};
//判断是否包含某个节点
BST.prototype.contain=function(key){
    function containRoot(node,key){
        if(node===null){   //树为空
            return false;
        }
        if(node.key===key){
            return true;
        }else if(node.key>key){  //查找的key值小于当前key值,从左子树中查找
            return containRoot(node.left,key);
        }else{        //查找的key值大于当前key值,从右子树中查找
            return containRoot(node.right,key);
        }
    }
    return containRoot(this.root,key);
};
//查找节点
BST.prototype.search=function(key){
    function searchRoot(node,key){
        if(node===null)
            return null;
        if(node.key===key){
            return node.value;
        }else if(node.key>key){
            return searchRoot(node.left,key);
        }else{
            return searchRoot(node.right,key);
        }
    }
    return searchRoot(this.root,key);
}
//前序遍历
BST.prototype.preOrder=function(){
    var arr=[];
    function pre(node){
        if(node!==null){
            arr.push(node.key+': '+node.value);
            pre(node.left);
            pre(node.right);
        }
    }
    pre(this.root);
    return arr;
};
//中序遍历:  二叉搜索树的中序遍历为排好序的序列
BST.prototype.inOrder=function(){
    var arr=[];
    function ino(node){
        if(node!==null){
            ino(node.left);
            arr.push(node.key+': '+node.value);
            ino(node.right);
        }
    }
    ino(this.root);
    return arr;
};
//后序遍历
BST.prototype.afterOrder=function(){
    var arr=[];
    function after(node){
        if(node!==null){
            after(node.left);
            after(node.right);
            arr.push(node.key+': '+node.value);
        }
    }
    after(this.root);
    return arr;
};
//层序遍历   广度优先:用队列实现,先将根节点入队,再循环,循环时每次将队首出队,将出队元素的子节点依次入队,直到队列为空
BST.prototype.levelOrder=function(){
    var arr=[],list=[];
    arr.push(this.root);
    while(arr.length!==0){
        var top=arr[0];
        arr.shift();   //将队首出队
        list.push(top.key+': '+top.value);  //记录出队顺序,即层序遍历顺序
        if(top.left){
            arr.push(top.left);
        }
        if(top.right){
            arr.push(top.right);
        }
    }
    return list;
};
//寻找键值最小的节点,即最左边的结点
BST.prototype.min=function(){
    if(this.count===0){
        return null;
    }
    function minNode(node){
        while(node.left){
            node=node.left;
        }
        return node.key+': '+node.value;
    }
    return minNode(this.root);
};
//寻找键值最大的节点,即最右边的结点
BST.prototype.max=function(){
    if(this.count===0){
        return null;
    }
    function maxNode(node){
        while(node.right){
            node=node.right;
        }
        return node.key+': '+node.value;
    }
    return maxNode(this.root);
}
//删除键值最小的节点
BST.prototype.removeMin=function(){
    function removemin(node){
        //用最小节点的右孩子代替当前节点
        if(node.left===null){
            return node.right;
        }
        node.left=removemin(node.left);
        return node;
    }
    if(this.root!==null){
        this.root=removemin(this.root);
        this.count--;
    }
};
//删除键值最大的节点
BST.prototype.removeMax=function(){
    function removemax(node){
        //用最大节点的左孩子代替当前节点
        if(node.right===null){
            return node.left;
        }
        node.right=removemax(node.right);
        return node;
    }
    if(this.root!==null){
        this.root=removemax(this.root);
        this.count--;
    }
};
//删除指定的节点:   如果要删除的节点既有左子树,又有右子树,则替换节点为该节点右子树的最小值
BST.prototype.remove=function(key){
    var _this=this;
    function rem(node,key){
        if(node===null){
            return null;
        }
        if(key<node.key){
            node.left=rem(node.left,key);
            return node;
        } else if(key>node.key){
            node.right=rem(node.right,key);
            return node;
        }else{
            if(node.left===null){
                _this.count--;
                return node.right;
            }else if(node.right===null){
                _this.count--;
                return node.left;
            }else{
                var son=node.right.min();
                node.key=son.split(': ')[0];
                node.value=son.split(': ')[1];
                node.right.removeMin();
                _this.count--;
                return node;
            }
        }

    }
    this.root=rem(this.root,key);
};

点赞