Java实现一个最大堆

  • 复杂度分析
  • 最大堆代码
  • 参考文献



这是写的第一篇博客,在此之后对于学过的知识要经常性的做出总结!


用Java实现了一个最大堆,在堆内部创建了一个HeapSort的内部类用于堆排序。


堆中提供了插入和删除的基本方法,核心的方法是

下调方法


下调方法是将一个近似堆(即,仅首节点不满足堆属性特征),调整为一个堆。


还有上调方法,该方法用于在堆末尾插入元素后,将该元素调整到合适的位置,是数组任然满足堆属性特征。


关于主要方法的思路,在代码的注释中都给出了。

复杂度分析

下调算法和上调算法的最坏复杂度为O(log2n),即树的高度。
建堆方法中使用了约使用了n/2次的下调算法,故复杂度为O(nlog2n)。
堆排序算法使用建堆方法和n-1次下调算法,故复杂度为O(nlog2n)。
考虑了一种数组以从大到小排列的情况,这时候建堆的复杂度为O(n),从而堆排序的复杂度为O(n)。
注意:本篇文章暂时未考虑堆的动态扩容。

最大堆代码

package sort;

import java.util.Arrays;
import java.util.Random;


/**
 * 堆:
 * 1.完全二叉树;
 * 2.各结点满足堆次序属性(最大堆:每个节点中的数据项都大于或者等于其子女的数据项) 根节点从0开始编号, 完全二叉树的性质
 *         1.i的子结点2i+1,2i+2;父节点(i-1)/2 
 *         2.最后一个非叶子结点(n-1)/2(就是最后一个叶子节点的父节点)
 *	Heap:
 *	方法:
 *	Heap()
 *	Heap(int[])
 *	isEmpty()
 *	insert()
 *	peek()
 *	delete()
 *	deleteTop()
 *	Heapify()
 *
 *	内部类:
 *	HeapSort 用于堆排序
 *	 
 *	注:堆由数组实现,这里暂时不考虑堆的动态扩展
 *	
 *	一些方法的思路:
 * 1.下调
 * 作用于近似堆(仅有根节点不符合堆属性)
 * 1.1把根节点作为当前节点,
 * 1.2循环下调
 * 1.2.1找出左右子女中较大的那一个(还要考虑右子女是否存在)
 * 1.2.1.1右子女不存在,则较大的节点为左子女
 * 1.2.1.2右子女存在,比较两个节点的值,选出较大值的那个节点
 * 1.2.2将找出的节点和当前节点比较,
 * 1.2.2.1若当前节点<找出的节点,将两个节点值互换,当前节点的位置设为找出的节点位置
 * 1.2.2.2否则,退出循环
 * 结束条件:当前节点没有左右子女节点
 * 边界判断:
 * 当前节点有可能左子女节点不存在(即,右子女也会不存在)
 * 当前节点有可能右子女节点不存在,但是左子女存在
 * 当前节点左右子女节点均存在
 * 
 * 2.上调
 * 作用于堆,在堆末尾插入一个元素
 * 2.1将插入节点设为当前节点,判断当前节点是否大于其父节点
 * 2.1.1若大于将其和父节点换位,当前节点设为父节点,继续执行2.1
 * 2.1.2否则,结束函数
 * 结束条件:
 * 当前节点为根节点
 * 
 * 3.构建堆
 * 从最后一个非叶子节点((n-1)/2个节点)往根节点遍历,对每个节点使用下调算法
 * 
 * 4.堆排序
 * 4.1构建堆
 * 4.2 排序
 * 4.2.1 从n到1节点,取出节点作为当前节点i
 * 4.2.2 交换根节点和当前节点i,
 * 4.2.3 对0到i-1节点使用下调算法
 * 
 */
public class Heap {

	private int[] data;
	private int mySize;

	/**
	 * 构建一个空堆
	 */
	public Heap() {
		data = new int[100];
		mySize = 0;
	}
	
	/**
	 * 通过指定的数组构建堆
	 * @param arr
	 */
	public Heap(int[] arr){
		if (arr == null || arr.length == 0) {
			throw new RuntimeException("Invalid Parameters.");
		}
		data = new int[arr.length * 2];// 2倍扩容
		// 复制元素
		for (int i = 0; i < arr.length; i++) {
			data[i] = arr[i];
		}
		//设置mySize
		mySize = arr.length;
		//构建堆
		Heapify(data,mySize-1);
	}
	
	/**
	 * 返回堆中的数组
	 * @return
	 */
	public  int[] getData(){
		return Arrays.copyOfRange(data, 0, mySize);//返回一个副本
	}
	
	/**
	 * 返回堆的大小
	 * @return
	 */
	public int size(){
		return mySize;
	}
	
	/**
	 * 判断堆是否为空
	 * 
	 * @return
	 */
	public boolean isEmpty() {
		return mySize == 0;
	}

	/**
	 * 在堆的末尾插入一个元素
	 * 
	 * @return
	 */
	public boolean insert(int elem) {
		data[mySize++]=elem;
		percolate_up(data,0,mySize-1);//上调
		return true;
	}

	/**
	 * 返回堆顶的元素
	 * @return
	 */
	public int peek() {
		if (mySize == 0) {
			throw new RuntimeException("Heap is empty!");
		}
		return data[0];
	}

	/**
	 * 从末尾删除
	 * @return
	 */
	public int delete() {
		if (mySize == 0) {
			throw new RuntimeException("Heap is empty!");
		}
		int temp=data[mySize-1];
		data[--mySize] = 0;
		return temp;
	}
	
	/**
	 * 删除首节点
	 * 
	 */
	public int deleteTop(){
		if (mySize == 0) {
			throw new RuntimeException("Heap is empty!");
		}
		swap(data,0,mySize-1);
		int temp=data[mySize-1];
		percolate_down(data, 0, mySize-2);//下调,将近似堆恢复成堆
		data[--mySize]=0;
		
		return temp;
	}

	/**
	 * 从给定数组的start位置开始往end位置下调
	 * @param data
	 * @param start
	 * @param end
	 * 
	 * 复杂度:O(lgn)
	 */
	private void percolate_down(int[] data,int start,int end) {
		if (data == null || start < 0 || end >= data.length || start > end) {
			throw new RuntimeException("Invalid Parameters!");
		}

		int cur = start;
		int left_child = cur * 2 + 1;
		// 遍历的条件:当前节点的左子节点存在
		while (left_child <= end) {
			//1.找出左右子女中较大的节点
			int right_child=cur*2+2;
			int greater_child=left_child;//左右子女中较大的节点
			if(right_child<=end){
				if(data[left_child]<data[right_child])
					greater_child=right_child;
			}
			//其他情况,都是greater_child=left_child;
			
			//2.拿较大的节点和当前节点比较
			if(data[cur]<data[greater_child]){
				swap(data,cur,greater_child);
				
				cur=greater_child;//更新节点位置
				left_child=cur*2+1;
			}else{
				break;
			}
		}
	}

	/**
	 * end节点使得原来的堆属性无法维持,上调该节点,恢复堆属性
	 * @param data
	 * @param start 堆的根节点
	 * @param end	末尾节点
	 * 
	 * 复杂度:O(lgn)
	 */
	private void percolate_up(int[] data,int start,int end) {
		if (data == null || start < 0 || end >= data.length || start > end) {
			throw new RuntimeException("Invalid Parameters!");
		}

		int cur = end;
		while (cur != start) {
			int parent = (cur - 1) / 2;
			if (data[cur] > data[parent]) {
				swap(data, cur, parent);
				cur = parent;
			}else{
				break;
			}
			
		}
	}

	/**
	 * 构建堆
	 * 
	 * @param data
	 * @param end 数组的最后一个元素 
	 * 
	 * 复杂度O(nlgn)
	 */
	public void Heapify(int[] data, int end) {
		if (data == null || data.length == 0 || end <= 0) {
			throw new RuntimeException("Invalid Parameters!");
		}

		for (int i = (end - 1) / 2; i >= 0; i--) {
			percolate_down(data, i, end);
		}
	}
	
	/**
	 * 交换值
	 * @param arr
	 * @param index1
	 * @param index2
	 */
	public void swap(int[] arr,int index1,int index2){
		if (arr != null && index1 != index2 && index1 < arr.length
				&& index2 < arr.length) {
			int temp = arr[index1];
			arr[index1] = arr[index2];
			arr[index2] = temp;
		}
	}
	
	/**
	 * 显示array的内容
	 * 
	 * @param a
	 *            数组
	 */
	public static void showArray(int a[]) {
		for (int i = 0; i < a.length; i++) {
			if (i % 10 == 0)
				System.out.println();
			System.out.print(a[i] + "\t");
		}
	}
	
	/**
	 * 
	 *用于堆排序算法
	 *
	 *O(nlgn)
	 */
	public  class HeapSort{
		public void sort(int[] arr){
			//1.构建堆
			Heapify(arr, arr.length-1);
			for(int i=arr.length-1;i>=1;i--){
				swap(arr,0,i);
				percolate_down(arr, 0, i-1);
			}
		}
	}

	public static void main(String[] args) {
		// int[] a = { 4, 9, 8, 7, 6, 5 };
		// Heap heap = new Heap();
		// 下调测试
		// heap.percolate_down(a, 0, a.length-1);
		// Heap.showArray(a);

		// 上调测试
		// int[] b={10,9,8,7,6,11};
		// heap.percolate_up(b, 0, b.length-1);
		// Heap.showArray(b);

		// 构建堆测试
		// heap.Heapify(a, a.length-1);
		// Heap.showArray(a);

		// 堆测试
		// int[] a = { 4, 5, 6, 7, 8, 9, 10 };
		// Heap heap = new Heap(a);
		// System.out.println("heap size:" + heap.size());
		// System.out.println("Is heap empty?" + heap.isEmpty());
		// int[] data = heap.getData();
		// Heap.showArray(data);
		// // 插入
		// heap.insert(11);
		// heap.insert(12);
		// System.out.println("\nheap size:" + heap.size());
		// data = heap.getData();
		// Heap.showArray(data);
		// System.out.println();
		// //删除
		// heap.delete();
		// data = heap.getData();
		// Heap.showArray(data);
		// heap.deleteTop();
		// data = heap.getData();
		// Heap.showArray(data);
		// System.out.println("\nheap size:" + heap.size());
		
		//堆排序测试
		int[] a=new int[100];
		Random random=new Random();
		for(int i=0;i<a.length;i++){
			a[i]=random.nextInt(101);
		}
		
		HeapSort sort=new Heap().new HeapSort();
		sort.sort(a);
		Heap.showArray(a);
	}

}