堆排序

一、预备知识-堆

堆排序是利用这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

堆是具有以下性质的完全二叉树

  • 每个结点的值都大于或等于其左右孩子结点的值,称为大根堆;
  • 或者每个结点的值都小于或等于其左右孩子结点的值,称为小根堆。如下图:


通过图可以比较直观的看出大根堆和小根堆的特点,需要注意的是:这种结构是对父节点-左/右孩子节点之间做的约束,而对左-右孩子节点之间并没有什么要求。

另外,因为堆的结构是完全二叉树,所以可以用数组来存储,并通过节点下标的规律快速进行索引。

下面是上图大根堆与小根堆对应的数组:


二、堆排序基本思想图解(大根堆为例)

假设现在待排序数据存在array[count]中,其初始状态如下:


对应的完全二叉树为:


堆排序的过程如下:

(1)初始化堆;

因为堆是对父节点-左/右孩子节点之间的约束,所以从最后一个非叶子节点开始调整。


注意每次交换后,都要对下一层的子堆进行递归调整,因为交换后有可能破坏已调整子堆的结构。


(2)进行调整后,堆顶元素(array[0])为最大值,将最大值与堆尾部元素(array[count-1])交换,并将count值减去1,则此时得到新的无序数组array[count],此时的堆被破坏;


对应到数组元素为:

(黄色标记为已排序部分)


(3)调整堆:与建堆过程类似,堆顶元素被一个比较小的值代替,所以从堆顶元素开始调整,在堆顶、堆顶的左孩子、堆顶的右孩子中找出最大的与堆顶进行交换,被交换的元素再次与他下一层的左右孩子进行比较(递归)调整。



(4)重复(2)。

下面把整个过程画完:


此时,大概的一个手工过程就懂了,注意的是:初始化堆是基础,时从下向上调整。交换后调整堆时因为有了建堆的基础,每次调整的都是二叉树的一支子树,是从上往下。

三、代码实现

package Alog;

public class HeapSort {

	public static void main(String[] args) {
		int[] array = new int[]{12, 5, 9 , 36, 8, 21, 7};
		System.out.println("初始状态:");
		showArray(array);
		
		initHeap(array); //这个应该也是堆排序的一部分,此处只是为了显示下结果
		System.out.println("建堆之后:");
		showArray(array); 
		
		heapSort(array);
		System.out.println("排序之后:");
		showArray(array); 
	}
	
	
	public static void heapSort(int[] array){
		initHeap(array); //建堆
		
		int count = array.length;
		while(count > 1) {
			int tmp = array[count - 1];
			array[count - 1] = array[0];
			array[0] = tmp;
			
			count--; //未排序部分又少一个
			adjustHeap(array, count, 0);//调整过程自上而下,参数root=0
		}
	}	
	
	public static void initHeap(int[] array){
		//建堆,从最后一个非叶子节点开始,而最后一个非叶子节点的下标为array.length/2-1
		for(int root = array.length/2 - 1; root >= 0; root--){
			adjustHeap(array, array.length, root);
		}
		
	}
	

	public static void adjustHeap(int[] array, int count, int root){
		int maxChildIndex;
		
		while(root <= count/2-1) { //待调整子堆的根节点必须是非叶子节点
			//需要在root、letfchild、rightchild中找到最大的值,
			//因为最后一个非叶子节点有可能没有右孩子,所以要做出判断。
			if(root == count/2 - 1 && count % 2 == 0){
				//节点数量为偶数时,最后一个非叶子节点只有左孩子
				maxChildIndex = 2 * root + 1;				
			}else{
				int leftChildIndex = 2 * root + 1;
				int rightChildIndex = 2 * root + 2;
				
				maxChildIndex = array[leftChildIndex] > array[rightChildIndex] ?
						leftChildIndex : rightChildIndex;
			}
			
			if(array[root] < array[maxChildIndex]){
				int tmp = array[root];
				array[root] = array[maxChildIndex];
				array[maxChildIndex] = tmp;
				
				//*****************这里很重要,继续调整因交换结构改变的子堆
				root = maxChildIndex; 
			}else{
				return;
			}
		}
	}
	
	public static void showArray(int[] array){
		for(int i = 0; i < array.length; i++){
			System.out.print(array[i] + ((i == array.length - 1) ? "\n" : " "));
		}
	}
}

输出结果:

初始状态:
12 5 9 36 8 21 7
建堆之后:
36 12 21 5 8 9 7
排序之后:
5 7 8 9 12 21 36
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章