手写最大堆(Java实现)

最大堆

最大堆和最小堆是二叉堆的两种形式。
最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

最大堆的父元素与子元素的索引有如下关系

在最大堆中,设根节点索引从1开始,当父节点索引为i时,左孩子节点2*i , 右孩子索引2*i+1。

实现最大堆

定义最大堆的数据结构和构造函数
堆中数据的存储我们可以使用数组来实现,非常方便。

public class MaxHeap {

    private int data[];     //存放堆数据的数组
    private int size;       //当前堆的大小
    private int capacity;   //堆的最大容量

    public MaxHeap(int maxSize){
        //数组索引为0的位置不放元素
        data = new int[maxSize+1];
        this.size = 0;
        this.capacity = maxSize;
    }
}

向堆里面插入元素

向堆里插入元素,首先将数据放到数据的最后一个位置,然后通过与其父元素比较,不断上移,直到该元素处于正确的位置。

    //向堆里面插入元素
    public void insert(int d){
        if(size == capacity){
            System.out.println("堆已满!");
            return;
        }
        //索引为0的位置不存放元素
        data[size+1] = d;
        size++;
        //插入在最后的元素上移方法
        shiftUp(size);
    }

    //堆插入元素时的元素上移
    private void shiftUp(int i) {
        //数组可能越界问题始终不能忽视
        //当此元素比父元素大时,交换这两个元素位置
        while(i > 1 && data[i] > data[i/2]){
            int t = data[i];
            data[i] = data[i/2];
            data[i/2] = t;
            i /= 2;
        }
    }

删除堆中的最大元素

最大堆删除操作只能删除最大元素,具体做法时将最后一个元素放到第一个元素位置(替换最大元素),然后将这个元素不断下移到恰当位置,下面定义的shiftDown就是元素下移的方法。

    //删除堆的最大元素
    public int deleteMax(){
        if(size == 0){
            System.out.println("堆已经是空的了!");
            return -1;
        }
        int t = data[1];
        //将最后一个元素放到第一个元素位置
        data[1] = data[size];
        size--;
        //然后将第一个元素下移到适当位置
        shiftDown(1);
        return t;
    }

    //堆删除元素时的元素下移
    private void shiftDown(int i) {
        // TODO Auto-generated method stub
        while(2*i <= size){
            // 将要将data[i]与data[j]交换
            int j = 2*i;
            // 让j指向他的孩子结点中的大的那一个
            if(j+1 <= size && data[j] < data[j+1]){
                j += 1;
            }
            if(data[i] > data[j])
                break;
            //元素下移
            int t = data[i];
            data[i] = data[j];
            data[j] = t;
            i = j;
        }
    }

堆排序

先将数组中的元素存放堆中去,然后依次删除取出堆中的最大元素就能实现堆排序

//堆排序
    public void heapSort(int arr[],MaxHeap heap){
        for(int i = 0; i < arr.length; i++){
            heap.insert(arr[i]);
        }
        for(int i = arr.length-1; i >=0 ; i--){
            arr[i] = heap.deleteMax();
        }
    }

堆排序优化
上面的那个堆排序需要先将数组中的数存到堆中,这里的时间复杂度是n(log2n),可以通过改变建堆的过程从而将建堆的时间复杂度变为O(n)级别。

使用heapify建堆:

public MaxHeap(int arr[],int maxSize){
        this.data = new int[maxSize+1];
        this.capacity = maxSize;
        for(int i = 0; i < arr.length; i++){
            data[i+1] = arr[i];
        }
        this.size = arr.length;
        for(int i = size; i >= 1; i--){
            shiftDown(i);
        }
    }

这里能够提高效率是因为,从最后的元素开始shiftDown,实际上差不多有一半的元素不需要移动,从而节省了时间。改进后的堆排序代码为:

//bottom-up堆排序O(n)+O(nlog2n)
    public void heapSort2(int arr[]){
        MaxHeap maxHeap = new MaxHeap(arr,100);
        for(int i=arr.length-1; i >= 0 ; i--){
            arr[i] = maxHeap.deleteMax();
        }
    }

你可能感兴趣的:(数据结构与算法)