数据结构—堆
堆是一棵完全二叉树,其结点含有Comparable的对象。堆又分为最大堆和最小堆:
- 最大堆:每个结点的对象都大于等于它的子孙结点的对象
- 最小堆:每个结点的对象都小于等于它的子孙结点的对象
在本文章只讨论最大堆以及其实现。
如果一棵树是完全二叉树,则可以使用数组来表示完全二叉树。下图的完全二叉树是一个最大堆,使用数组的形式来实现最大堆:将完全二叉树的层序遍历的结果放到从索引1开始的连续数组位置中。(从索引1开始可以显得实现略为简单)
这样,我们就很容易找到一个结点的双亲或孩子中的数据,则数组索引
i 处的结点:
- 双亲在索引 i/2 处,除非该结点是跟结点(i=1)
- 孩子在索引 2i 与 2i+1 处
插入元素
最大堆中插入新元素后,要保证仍然是最大堆。如上图最大堆,要插入新元素85,要从下一个可用于叶子的空闲位置开始。由于85>30,就将30移到新孩子的位置,将原来含30的结点看成是空的;由于85大于该空结点的双亲80,就将80移到空结点;由于85不大于下一个双亲90,则将新元素放到空结点中。
从表示堆的数组的角度展示同样的插入过程。索引10处有可用于新元素的空间,该位置双亲为索引5元素,由于85>30,则将30移到索引10处,余下步骤类似。
删除根
删除根后要保证仍然是最大堆。删除根会留下两棵不相连的二叉树,相反我们会先将最后叶子结点的元素赋值给根,再删除该叶子结点,得到一个
半堆,随后将它转化为堆。
下面是将30赋值给根,然后删除该叶子结点,得到半堆,再转化为堆。如下是
半堆转化为堆的过程。
创建堆
- 使用add方法:将每个对象插入到初始化为空的堆,既然add是O(log n)的操作,则以这种方法创建堆就是O(n log n)
- 使用reheap:将堆的元素从索引1开始放进数组中,然后从数组末端的第一个非叶子结点(位于索引lastIndex/2处)开始,使用reheap函数将半堆转化为堆。该方法比较高效,复杂度为O(n)。
for(int rootIndex=lastIndex/2;rootIndex>0;rootIndex--) reheap(rootIndex);
- 用另一个构造函数:构造输入数组的构造函数,将元素复制到数据域heap,然后使用reheap创建堆。
public MaxHeap(T[] entries){ heap=(T[]) new Comparable[entries.length+1]; lastIndex=entries.length; for(int index=0;index<entries.length;index++) heap[index+1]=entries[index]; for(int rootIndex=lastIndex/2;rootIndex>0;rootIndex--) reheap(rootIndex); }
堆排序
基本思想:
- 先将待排序数组建成一个最大堆,此堆为初始的无序区
- 将堆顶元素与无序区最后一个元素交换,由此得到新的无序区和有序区
- 重复以上两个过程,直到无序区只有一个元素为止
下面是一个堆排序的例子:
实现代码
public interface MaxHeapInterface <T extends Comparable<? super T>>{
public void add(T newEntry);
public T removeMax();
public T getMax();
public boolean isEmpty();
public int getSize();
public void clear();
}
public class MaxHeap<T extends Comparable<? super T>> implements MaxHeapInterface<T>,Serializable {
private T[] heap;
private int lastIndex;
private static final int DEFAULT_INITIAL_CAPACITY=25;
public MaxHeap() {
this(DEFAULT_INITIAL_CAPACITY);
}
public MaxHeap(int initialCapacity) {
heap=(T[]) new Comparable[initialCapacity+1];
lastIndex=0;
}
public MaxHeap(T[] entries){
heap=(T[]) new Comparable[entries.length+1];
lastIndex=entries.length;
for(int index=0;index<entries.length;index++)
heap[index+1]=entries[index];
for(int rootIndex=lastIndex/2;rootIndex>0;rootIndex--)
reheap(rootIndex);
}
private void doubleArray(){
T[] oldheap=heap;
heap=(T[]) new Comparable[2*oldheap.length];
System.arraycopy(oldheap, 0, heap, 0, oldheap.length);
}
@Override
public void add(T newEntry) {
System.out.print(newEntry+" ");
lastIndex++;
if(lastIndex>=heap.length)
doubleArray();
int newIndex=lastIndex;
int parentIndex=newIndex/2;
while(newIndex>1 && newEntry.compareTo(heap[parentIndex])>0){
heap[newIndex]=heap[parentIndex];
newIndex=parentIndex;
parentIndex=newIndex/2;
}
heap[newIndex]=newEntry;
}
private void reheap(int rootIndex){
boolean done=false;
T orphan=heap[rootIndex];
int largerChildIndex=2*rootIndex;
int rightChildIndex;
while(!done && largerChildIndex<=lastIndex){
rightChildIndex=largerChildIndex+1;
if(rightChildIndex<=lastIndex && heap[rightChildIndex].compareTo(heap[largerChildIndex])>0)
largerChildIndex=rightChildIndex;
if(orphan.compareTo(heap[largerChildIndex])<0){
heap[rootIndex]=heap[largerChildIndex];
rootIndex=largerChildIndex;
largerChildIndex=2*rootIndex;
}else
done=true;
}
heap[rootIndex]=orphan;
}
private static<T extends Comparable<? super T>> void reheap(T[] heap,int rootIndex,int lastIndex){
boolean done=false;
T orphan=heap[rootIndex];
int leftChildIndex=2*rootIndex+1;
while(!done && leftChildIndex<=lastIndex){
int largerChildIndex=leftChildIndex;
int rightChildIndex=leftChildIndex+1;
if(rightChildIndex<=lastIndex && heap[rightChildIndex].compareTo(heap[largerChildIndex])>0)
largerChildIndex=rightChildIndex;
if(orphan.compareTo(heap[largerChildIndex])<0){
heap[rootIndex]=heap[largerChildIndex];
rootIndex=largerChildIndex;
leftChildIndex=2*rootIndex+1;
}else
done=true;
}
heap[rootIndex]=orphan;
}
public static <T extends Comparable<? super T>> T[] heapSort(T[] array,int n){
for(int rootIndex=n/2-1;rootIndex>=0;rootIndex--)
reheap(array, rootIndex, n-1);
swap(array, 0, n-1);
for(int lastIndex=n-2;lastIndex>0;lastIndex--){
reheap(array, 0, lastIndex);
swap(array, 0, lastIndex);
}
return array;
}
private static<T extends Comparable<? super T>> void swap(T[] array, int first,int last){
T temp=array[first];
array[first]=array[last];
array[last]=temp;
}
@Override
public T removeMax() {
T root=null;
if(!isEmpty()){
root=heap[1];
heap[1]=heap[lastIndex];
lastIndex--;
reheap(1);
}
return root;
}
@Override
public T getMax() {
T root=null;
if(!isEmpty())
root=heap[1];
return root;
}
@Override
public boolean isEmpty() {
return lastIndex<1;
}
@Override
public int getSize() {
return lastIndex;
}
@Override
public void clear() {
for(;lastIndex>-1;lastIndex--)
heap[lastIndex]=null;
lastIndex=0;
}
public void display(){
System.out.print("最大堆:");
for(int i=1;i<=lastIndex;i++)
System.out.print(heap[i]+" ");
System.out.println();
}
}
public class main_Heap {
private static MaxHeap<Integer> heap;
private static Integer a[]=new Integer[15];
public static void main(String[] args) {
heap=new MaxHeap<Integer>();
Random ra=new Random();
for(int i=0;i<15;i++)
heap.add(ra.nextInt(100));
System.out.println();
heap.display();
heap.removeMax();
heap.display();
for(int i=0;i<15;i++)
a[i]=ra.nextInt(100);
heap=new MaxHeap<Integer>(a);
heap.display();
a=heap.heapSort(a, a.length);
System.out.print("堆排序:");
for(int i=0;i<a.length;i++)
System.out.print(a[i]+" ");
}
}
结果:
36 64 15 51 54 69 46 8 68 20 72 25 11 67 84
最大堆:84 69 72 54 68 25 67 8 36 20 51 15 11 46 64
最大堆:72 69 67 54 68 25 64 8 36 20 51 15 11 46
最大堆:97 58 95 46 53 61 58 1 36 39 4 6 28 29 41
堆排序:1 4 6 28 29 36 39 41 46 53 58 58 61 95 97
最大堆:84 69 72 54 68 25 67 8 36 20 51 15 11 46 64
最大堆:72 69 67 54 68 25 64 8 36 20 51 15 11 46
最大堆:97 58 95 46 53 61 58 1 36 39 4 6 28 29 41
堆排序:1 4 6 28 29 36 39 41 46 53 58 58 61 95 97