MaxHeap

用二叉树实现堆:

 

用数组存储二叉堆:

 

基于数组实现二叉堆:

package com.lt.datastructure.MaxHeap;

public class MaxHeap<E extends Comparable<E>> {
   private Array1<E> data;

    public MaxHeap(int capacity) {
       data = new Array1<>(capacity);
    }

    public MaxHeap() {
       data = new Array1<>();
    }
    
    public int size() {
        return data.getSize();
    }
   
    public boolean isEmpty() {
         return data.isEmpty();
    }
    
    //返回一个索引的父亲结点的索引
    private int parent(int index){
        if(index==0){
            throw new IllegalArgumentException("index 0 doesn't have parent.");
        }
        return (index-1)/2;
    }
    
    //返回完全二叉树数组表示中,一个索引所表示的元素的左孩子结点的索引
    private int leftChild(int index){
        return index*2+1;
    }
    
    //返回完全二叉树数组表示中,一个索引所表示的元素的右孩子结点的索引
    private int rightChild(int index){
        return index*2+2;
    }
    
    //向堆中添加元素siftUp
    public void add(E e){
        //添加元素
        data.addLast(e);
        //给添加的新元素e设置索引为data.getSize()-1;
        siftUp(data.getSize()-1);
    }

    private void siftUp(int k) {        
        while(k>0 && data.get(parent(k)).compareTo(data.get(k))<0){
            //如果父亲结点的值比孩子结点的值小,交换索引处的值
            data.swap(k,parent(k));
            //继续while循环判断新的父亲结点是否符合规则
            k  = parent(k);
        }
    }
    
    //看堆中的最大元素,索引大的为父亲结点,所以0处的值最大
    public E findMax(){
        if(data.getSize()==0){
            throw new IllegalArgumentException("Can not finMax when heap is null");        
        }
        return data.get(0);//索引为0的元素
    }
    //删除堆中最大元素并返回
    public E extractMax(){
        E ret = findMax();    
        data.swap(0,data.getSize()-1);//交换索引处的值
        data.removeLast();//删除了堆中最大元素
        siftDown(0);//下沉
        
        return ret;//将被删除的最大值返回
    }
    //完成下沉操作
    //找到左右孩子中较大值j,如果k所在的值大于j所在的值,那么结束循环,否则继续下潜
    private void siftDown(int k) {
        //当k为叶子结点,循环结束,即左孩子索引越界(右孩子结点比左孩子大)
        while(leftChild(k)<data.getSize()){
            int j = leftChild(k);
            //如果有右孩子,且右孩子大于左孩子的值,那么j为右孩子
            if(j + 1<data.getSize() && data.get(j+1).compareTo(data.get(j))>0){
                j ++;
            }
            //如果k没右孩子,或者有右孩子但值左孩子值比左孩子小,此时data[j]是左右孩子中较大值
            //此时如果k大于等于左右孩子的最大值,那么没有违反堆的性质,下沉操作结束
            if(data.get(k).compareTo(data.get(j))>=0){
                break;
            }
            //否则违反了堆的性质,继续交换
            data.swap(k, j);
            k = j;
        }
    }
   
    /*  replace:取出最大元素且替换成新的元素e,返回被替换的最大元素
     *     实现方法: 1 先extract,再add  复杂度 :两次logn ×
     *          2 直接替换,然后siftDown  复杂度:一次 logn ✔  
     */
    public E replace(E e){
        E ret = findMax();
        //将堆顶换成新元素e,但可能违背堆的性质,所以需要进行下潜操作
        data.set(0, e);
        siftDown(0);
        return ret;
    }
    /*  heapify(堆化,转化为堆):将任意数组整理成堆的形状
     *  只要合理的交换数组中的位置,其实也可以完成这样的操作,但是有更快的操作
     *  1 扫描数组,放入堆 × (将n个元素逐个放入空堆中,复杂度O(nlogn))
     *  2 抛弃叶子节点。从(倒数第一个非叶子节点的索引:最后一个节点的父亲结点)依次向前遍历,进行siftDown,从而变成一个最大堆 ✔ 
     *   heapify的过程: 算法复杂度为O(n)
     */
    public MaxHeap(E[] arr){
        //此处在动态数组新建了构造
        data = new Array1<>(arr);
        //从最后一个结点的父亲结点开始向前遍历,对每个元素做下沉操作
        for(int i = parent(arr.length-1) ; i>=0 ; i--){
            siftDown(i);
        }
    }
    @Override
    public String toString() {
        return "MaxHeap [data=" + data + "]";
    }
    
       
} 

 

堆测试:

 

数组的堆化测试:

posted @ 2018-12-02 09:56  IslandZzzz  阅读(226)  评论(0编辑  收藏  举报