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); };