堆排序

堆排序(Heapsort)

          堆分为大根堆和小根堆,是完全二叉树大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,根据大根堆的要求可知,最大的值一定在堆顶。

堆排序步骤示例

(1)、建堆: 建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中因为A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1转到图2。

(2)、调堆:如果初始数组是非降序排序,不需要调堆,本身满足堆的定义,运行时间为O(1),为最好情况;如果初始数组是如图5,只有A[0] = 1不满足堆的定义,经过与子节点的比较调整到图6,但是图6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。如果递归调堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。建堆完成之后,堆如图7是个大根堆。将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图8,然后AdjustHeap(A, heapLen-1, 0),如图9。如此交换堆的第一个元素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图12。


注:子节点在数组中的下标与父节点的在数组中的下标之间的关系

堆排序示例代码

/**
 *  堆排序
 * @author bianjie
 *
 */
public class Heapsort {

	/**
	 * 建堆
	 * 
	 * @param data
	 *            数据
	 * @param length
	 *            数据长度
	 */
	public static void buildHeap(int data[], int length) {

		// 起始的第一个父节点
		int startIndex = length / 2 - 1;
		for (; startIndex >= 0; startIndex--) {
			// 调堆
			adjustHeap(data, length ,startIndex);
		}
	}

	/**
	 * 调整堆的大小位置
	 * @param data 数据数组
	 * @param dataLength 数据长度
	 * @param parentIndex 父节点,需注意父节点与子节点的关系得到的下标
	 */
	public static void adjustHeap(int data[], int dataLength, int parentIndex) {
		//因为数组的首个下标为0,而堆中对应的节点比起大1,所以计算左右子节点的时候需注意
		//不能直接用2*parentIndex和(2*parentIndex+1)作为左右节点在数组中的下标
		//当然你也可以一开始就调节他们之间下标一一对应关系,就可以用
		// 子左节点下标
		int leftChildIndex = 2 * (parentIndex + 1) - 1;
		// 子右节点下标
		int rightChildIndex = 2 *(parentIndex + 1);
		// 节点中的最大值的下标,初始设置为父节点为最大值
		int maxValueIndex = parentIndex;
		while (leftChildIndex <= dataLength || rightChildIndex <= dataLength) {
			// 左节点存在且大于父节点
			if (leftChildIndex < dataLength && data[leftChildIndex] > data[maxValueIndex]) {
				maxValueIndex = leftChildIndex;
			}
			// 右节点存在且大于父节点
			if (rightChildIndex < dataLength &&  data[rightChildIndex] > data[maxValueIndex]) {
				maxValueIndex = rightChildIndex;
			}
			// 存在子节点的值比父节点的值大,交换两者的值
			if (maxValueIndex != parentIndex) {
				// 进行数据位置交换
				swapData(data, parentIndex, maxValueIndex);
				// 打印数据
				printData(data);
				adjustHeap(data,dataLength,parentIndex);
				// 打乱了事先的低端排序,需要重新进行检验是否满足堆排序
				parentIndex = maxValueIndex;
				leftChildIndex =  2 * (parentIndex + 1) - 1;
				rightChildIndex = 2 * (parentIndex + 1);
			}else{
				break;
			}
		}
	}

	/**
	 * 排序
	 * @param data 数据数组
	 */
	public static void sortHeap(int data[]) {
		int dataLength = data.length;
		// 建堆
		buildHeap(data, dataLength);
		//辅助显示排序的结果标志
		int time = 1;
		while (dataLength > 1) {
			//一趟排序完得到的堆顶的最大值进行首尾位置调换,
			//同时将数据长度减一,起到保存已经得到的最大值的数据数列位置
			swapData(data, 0, --dataLength);
			printData(data);
			System.out.println("------ 第   "+(time++)+" 趟排序完成---");
			//重新进行建堆,调堆
			buildHeap(data, dataLength);
		}
	}

	/**
	 * 打印数据
	 * 
	 * @param data
	 */
	public static void printData(int data[]) {
		for (int i : data) {
			System.out.print(i + " ");
		}
		System.out.println();
	}

	
	/**
	 * 交换数据
	 * 
	 * @param data 数据数组
	 * @param firstIndex 要交换的第一个数据下标
	 * @param secondIndex 要交换的第二个数据下标
	 */
	public static void swapData(int[] data, int firstIndex, int secondIndex) {
		int temp = data[firstIndex];
		data[firstIndex] = data[secondIndex];
		data[secondIndex] = temp;
	}

	
	public static void main(String[] args) {
		int data[] = { 1, 3, 4, 5, 7, 2, 6, 8, 0 };
		printData(data);
		sortHeap(data);

	}

}


运行结果


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值