数据结构—堆

数据结构—堆

堆是一棵完全二叉树,其结点含有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 



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值