使用Java实现最大堆

来源:互联网 发布:win7开软件盘 编辑:程序博客网 时间:2024/05/21 20:34

最大堆和最小堆是二叉堆的两种形式。

最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

堆实际上是一种完全二叉树。最大(小)堆建堆的复杂度是o(n),也就是说可以在线性时间内把一个无序的序列转化为堆。插入的时间复杂度为o(log2(n))(2为底n的对数的时间复杂度),删除的时间复杂度也是这么多,因为对于插入删除,在最坏的情况下,插入将整个树从最低的一端比较到树的最高一端,而删除是从树的最高的一端比较到树最低的一端。

PS、这里有个疑问,就是从算法上大致看过去,会认为建堆的复杂度是o(nlog2(n)),实际计算出来结果是o(n),证明过程如下:

建堆的计算时间为2^i(log2(n)-1)从i=0加到i=log2(n),因为第i层的结点最多2^i个,每个元素在建堆调整位置的时候最坏情况是从最底层调整到最高层,因此第i层最坏情况下的总共调整次数应该是2^i(log2(n)-1),这个结果经过整理计算出来实际上复杂度是o(n)。


在之前我们用Java实现了二叉树的顺序存储和链式存储,这次用Java实现的最大堆是基于前面写的链式存储的二叉树实现的。


在实现最大堆的时候,需要在之前的TreeNode<E>类的基础上加上一个变量num表示当前结点在堆中的编号,而且要求泛型要是整形,所以我定义了一个HeapNode继承了TreeNode<Integer>(优先考虑复用,但是这里明显出现了HeapNode is TreeNode<Integer>的情形,因此采用继承):

package com.maxHeap;import com.linkedTree.TreeNode;public class HeapNode extends TreeNode<Integer> {//HeapNode is TreeNode<Integer>    private HeapNode leftChild;    private HeapNode rightChild;    private int num;//编号    public HeapNode(Integer e) {        super(e);    }    public int getNum() {        return num;    }    public void setNum(int num) {        this.num = num;    }    @Override    public TreeNode<Integer> getRightChild() {        return rightChild;    }    @Override    public void setRightChild(TreeNode<Integer> rightChild) {        this.rightChild = (HeapNode) rightChild;    }    @Override    public TreeNode<Integer> getLeftChild() {        return this.leftChild;    }    @Override    public void setLeftChild(TreeNode<Integer> leftChild) {        this.leftChild = (HeapNode) leftChild;    }    }

紧接着,同理写了一个MaxHeap类继承LinkedTree<Integer>:

package com.maxHeap;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Set;import com.linkedTree.LinkedTree;import com.linkedTree.TreeNode;public class MaxHeap extends LinkedTree<Integer> {//MaxHeap is LinkedTree<Integer> MaxHeap要求是完全二叉树//    private int size;//不用定义size,因为nodeMap.size()就是树的大小    private Map<Integer,HeapNode> nodeMap = new HashMap<Integer,HeapNode>();//编号与结点的映射    private void setRoot(HeapNode root){        this.root = root;    }    @Override    //重写getParent方法 因为节点有编号之后 有更快的方法获得父节点    public TreeNode<Integer> getParent(TreeNode<Integer> node) {//获取node结点的父节点        HeapNode heapNode = (HeapNode)node;         int nodeNum = heapNode.getNum();        return (nodeNum%2==0)?nodeMap.get((nodeNum>>1)-1):nodeMap.get((nodeNum-1)>>1);//获取nodeMap中对应结点 采用位运算增加效率    }    private void swapNode(HeapNode node1 , HeapNode node2){//交换node1和node2以及它的映射        int item1 = node1.getItem();        node1.setItem(node2.getItem());        node2.setItem(item1);        nodeMap.put(node1.getNum(), node1);        nodeMap.put(node2.getNum(), node2);    }    public void add(int data){//添加一个元素        HeapNode heapNode = new HeapNode(data);        shiftUpNode(nodeMap.size(),heapNode);//从最新的位置移到应该放的位置    }    public void add(Set<Integer> dataSet){//添加一个集合 迭代添加集合中的每个元素        Iterator<Integer> i = dataSet.iterator();        while(i.hasNext()){            add(i.next());        }    }    public MaxHeap(Set<Integer> set){//通过一个set创建一个MaxHeap        Iterator<Integer> i = set.iterator();        int num = 0;        while(i.hasNext()){            Integer e = i.next();            HeapNode node = new HeapNode(e);            shiftUpNode(num, node);//从最新的一个位置的结点移到他应该在的位置            num++;        }    }    private void shiftUpNode(int num, HeapNode node) {//从最靠后的结点移到准确的位置        if(this.root==null){            this.setRoot(node);//没有根则设置根            node.setNum(num);//设置编号        }        else{            boolean right = (num%2 == 0);//是否是父节点的右子树            if(right){                nodeMap.get((num>>1)-1).setRightChild(node);            }            else{                nodeMap.get((num-1)>>1).setLeftChild(node);            }            node.setNum(num);            HeapNode heapNode = node;//中间量            while(this.getParent(heapNode).getItem()<heapNode.getItem()){//构建最大堆                swapNode((HeapNode) this.getParent(heapNode),heapNode);//交换节点                heapNode = (HeapNode) this.getParent(heapNode);//交换完之后 保存父节点的位置 继续与其父节点进行比较                if(this.getParent(heapNode)==null)                    break;            }        }        nodeMap.put(num, node);//保存映射    }    private void shiftDownNode(){//把根节点下移到准确的位置        HeapNode pointer = (HeapNode) root;        while(true){            HeapNode leftChild = (HeapNode) pointer.getLeftChild();            HeapNode rightChild = (HeapNode) pointer.getRightChild();            boolean left = leftChild != null;            boolean right = rightChild != null;            if(left&&(pointer.getItem()<leftChild.getItem())||(right&&pointer.getItem()<rightChild.getItem())){//有左结点或者右结点并且可交换                if(left&&right){//左右节点都有                    if(leftChild.getItem()<rightChild.getItem()){//谁大谁上                        swapNode(pointer,(HeapNode) pointer.getRightChild());                        pointer = (HeapNode) pointer.getRightChild();//继续跟踪被下放的根节点                    }                    else{                        swapNode(pointer,(HeapNode) pointer.getLeftChild());                        pointer = (HeapNode) pointer.getLeftChild();                    }                }                else if(!left){//没有左结点                    swapNode(pointer,(HeapNode) pointer.getRightChild());                    pointer = (HeapNode) pointer.getRightChild();                }                else{//没有右结点                    swapNode(pointer,(HeapNode) pointer.getLeftChild());                    pointer = (HeapNode) pointer.getLeftChild();                }            }            else                return;        }    }    public int peek(){//获取最大的结点 不删除        return this.root.getItem();    }        public int pop(){//获取最大的结点 并且删除        int size = nodeMap.size();//映射个数        swapNode((HeapNode) this.root, nodeMap.get(size-1));//交换根结点和最后一个结点        int maxNum = nodeMap.get(size-1).getItem();//最大值        if((size-1)%2==0)//最后一个结点是父节点的右结点            getParent(nodeMap.get(size-1)).setRightChild(null);//将父节点的右结点置空 help gc 下同        else            getParent(nodeMap.get(size-1)).setLeftChild(null);        nodeMap.remove(size-1);//删除最大的        shiftDownNode();//将根结点调到适当的位置        return maxNum;    }}

最后写一个测试类Test来进行测试:

class Test{    public static void main(String[] args){        System.out.println("MaxHeap Test:");        MaxHeap maxHeap = new MaxHeap(set);        maxHeap.breadFirstOrder();        maxHeap.preOrder();        System.out.println(maxHeap.getRoot().getItem());        maxHeap.add(9);        maxHeap.breadFirstOrder();        Set<Integer> set2 = new HashSet<Integer>();        set2.add(9);        set2.add(11);        set2.add(10);        set2.add(19);        maxHeap.add(set2);        maxHeap.breadFirstOrder();        maxHeap.pop();               maxHeap.breadFirstOrder();    }}

我的实现思路在注释里。欢迎拍砖。