最大堆
最大堆和最小堆是二叉堆的两种形式。
最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。
最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。
最大堆的父元素与子元素的索引有如下关系
在最大堆中,设根节点索引从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();
}
}