堆与堆排序--递归与非递归java实现

不想你离开。 提交于 2019-12-04 22:28:34

堆排序是利用完全二叉树的结构思想,对线性数组进行排序的。

最大堆进行升序排序的基本思想:
① 初始化堆:将数列a[0...n-1]构造成最大堆。
② 交换数据:将a[0]和a[n-1]交换,使a[n-1]是a[0...n-1]中的最大值;然后将a[0...n-2]重新调整为最大堆。 接着,将a[0]和a[n-2]交换,使a[n-2]是a[0...n-2]中的最大值;然后将a[0...n-3]重新调整为最大值。 依次类推,直到整个数列都是有序的。

在第一个元素的索引为 0 的情形中:
性质一:索引为i的左孩子的索引是 (2*i+1);
性质二:索引为i的左孩子的索引是 (2*i+2);
性质三:索引为i的父结点的索引是 floor((i-1)/2);

堆分为大顶堆和小顶堆,

满足Key[i]>=Key[2i+1]&&key>=key[2i+2]称为大顶堆,

满足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]称为小顶堆

import java.util.Arrays;

/**
 * 堆与堆排序
 * @author mine_song 2017.04.05
 */
public class HeapSort {
	public static void main(String[] args) {
		int arr[] = { 4, 5, 2, 1, 8, 10, 2 };
		System.out.println("before sort:" + Arrays.toString(arr));
		heapSortAsc(arr, arr.length);
		System.out.println("after sort:" + Arrays.toString(arr));
	}

	/**
	 * 数组从0作为第一个结点,从下往上,从右往左, 第一个非叶结点为len / 2 - 1下标位置
	 * 
	 * @param arr
	 *            需要建立大顶堆的数组
	 * @param len
	 *            数组的长度
	 */
	public static void buildMaxHeap(int arr[], int len) {
		// 从(n/2-1) --> 0逐次遍历。遍历之后,得到的数组实际上是一个大顶堆。
		for (int i = len / 2 - 1; i >= 0; i--)
			// adjustDown(arr, i, len)
			adjustHeapWithRecur(arr, i, len);
	}

	/**
	 * 大顶堆的向下调整算法 (非递归实现)
	 *  注:数组实现的堆中, 第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
	 * 其中,N为数组下标索引值,如数组中第1个数对应的N为0。
	 * 
	 * @param arr
	 *            待排序的数组
	 * @param start
	 *            被下调节点的起始位置(一般为0,表示从跟元素开始)
	 * @param end
	 *            截至范围(一般为数组中最后一个元素的索引)
	 */
	public static void adjustDown(int[] arr, int start, int end) {
		int cur = start;// 当前(current)节点的位置
		int temp = arr[cur];
		int left = 2 * start + 1;// left左孩子,left + 1右孩子

		for (; left <= end; cur = left, left = 2 * left + 1) {
			// 选择左右孩子较大者
			if (left < end && arr[left] < arr[left + 1])
				left++;
			// 当前结点和左右孩子大小相比较
			if (temp >= arr[left])
				break;
			// 当前结点比左右孩子的较大者小
			else {
				// 交换
				arr[cur] = arr[left];
				arr[left] = temp;
			}
		}
	}

	/**
	 * 大顶堆的向下调整算法 (递归实现)
	 * 
	 * @param arr
	 * @param start
	 * @param end
	 */
	public static void adjustHeapWithRecur(int[] arr, int start, int end) {
		int temp = arr[start];
		int left = 2 * start + 1;
		if (left >= end)
			return;
		if (left < end - 1 && arr[left] < arr[left + 1])
			left++;
		if (temp >= arr[left])
			return;
		else {
			arr[start] = arr[left];
			arr[left] = temp;
		}
		// 继续调整被破坏的子堆
		adjustHeapWithRecur(arr, left, end);
	}

	/**
	 * 升序排序
	 * 
	 * @param arr
	 *            排序数组
	 * @param len
	 *            数组长度
	 */
	public static void heapSortAsc(int arr[], int len) {
		buildMaxHeap(arr, len);
		// 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
		for (int i = len - 1; i > 0; i--) {
			// 把最大值放在数组的末尾
			swap(arr, i, 0);
			// 继续调整0~i-1使之成为最大堆
			adjustDown(arr, 0, i - 1);
		}
	}

	// 交换
	private static void swap(int[] arr, int i, int j) {
		arr[i] = arr[i] ^ arr[j];
		arr[j] = arr[i] ^ arr[j];
		arr[i] = arr[i] ^ arr[j];
	}
}
运行结果:

before sort:[4, 5, 2, 1, 8, 10, 2]
after sort:[1, 2, 2, 4, 5, 8, 10]

堆排序性能;

空间效率:常数辅助单元,空间复杂度为O(1)

时间效率:建堆时间是O(n),之后有n-1次向下调整堆,每次调整堆的时间复杂度为O(h),最好,最坏,平均复杂度是O(nlog)

稳定性:堆排序是一种选择排序,是不稳定的排序算法。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!