java代码之堆排序

1、堆排序的基本思想
堆排序是对直接选择排序算法的一种改进,其思想为:对一组待排序记录的关键字,首先把它们建成一个大根堆或小根堆,从而输出堆顶的最小关键字(假设利用小根堆来排序)。然后对剩余的关键字在建堆,便得到次小的关键字,如此反复进行,直到全部关键字排成有序序列为止。
2、堆排序图解
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。
堆是具有以下性质的完全二叉树:
(a)每个结点的值都大于或等于其左右孩子结点的值,称为大根堆;
(b)或者每个结点的值都小于或等于其左右孩子结点的值,称为小根堆。如下图:
在这里插入图片描述
通过图可以比较直观的看出大根堆和小根堆的特点,需要注意的是:这种结构是对父节点-左/右孩子节点之间做的约束,而对左-右孩子节点之间并没有什么要求。另外,因为堆的结构是完全二叉树,所以可以用数组来存储,并通过节点下标的规律快速进行索引。
下面是上图大根堆与小根堆对应的数组:
在这里插入图片描述
2.1堆排序基本思想图解(小根堆为例)

堆排序的过程如下:
(1)先建立一个大根堆:
在这里插入图片描述

(2)大根堆建立完成之后,堆顶元素(array[0])为最大值,将最大值与堆尾部元素(array[count-1])交换,并将count值减去1,则此时得到新的无序数组array[count],再次进行调整,相当于再次将array[0]到array[count-2]建大根堆,不断重复,直到只剩array[0]为止
在这里插入图片描述
3、堆排序核心算法


public class HeapSelect_Sort {
	public static void main(String[] args) {
		int[] array = { 49, 38, 65, 97, 76, 13, 27, 49 };
		heapSelect_Sort(array);
	}

	// 第一次构建大根堆 从下往上构建
	public static void firstBuildMaxHeap(int[] array) {
		// 通过算数右移,使half等于最后一个结点的父节点
		int half = (array.length - 1) >> 1;
		// 依次往上进行调整
		for (int i = half - 1; i >= 0; i--) {
			adjustMaxHeap(array, array.length, i);
		}
	}

	public static void adjustMaxHeap(int[] array, int length, int i) {
		int large = i;
		int left = 2 * i + 1;
		int right = 2 * i + 2;
		if (left < length && array[left] <array[large]) {
			large = left;
		}
		if (right < length && array[right] < array[large]) {
			large = right;
		}
		if (large != i) {
			exchange(array, large, i);
			// 交换后的该儿子结点再次进行调整
			adjustMaxHeap(array, length, large);
		}

	}

	// 排序
	public static void heapSelect_Sort(int[] array) {
		// 先建一个大堆
		firstBuildMaxHeap(array);
		// 从最后一个元素开始对序列进行调整,不断的缩小调整的范围直到第一个元素
		for (int i = array.length - 1; i >= 0; i--) {
			// 把最大值放在数组的末尾
			exchange(array, 0, i);
			// 继续调整使之成为最大堆
			adjustMaxHeap(array, i, 0);
		}
		for (int i : array) {
			System.out.print(" " + i);
		}
	}

	// 交换方法
	public static void exchange(int[] array, int large, int i) {
		int temp = array[large];
		array[large] = array[i];
		array[i] = temp;
	}
}

4、堆排序性能分析
空间复杂度
堆排序是对直接选择排序的改进算法,选择排序的特点在于每次选取最小或最大的值,而选取最大值时的比较次数为复杂度的关键,堆排序采用二叉树的方法存储元素,每个节点都满足父节点的值大于等于子节点的特点,与直接选择排序类似,堆排序需要两个值的空间来存储临时变量,用于交换节点,一次用于存储子树最大节点用于交换子节点,一次用于存储堆顶的值用于交换最后的节点,所以空间复杂度为O(1)
时间复杂度
采用堆的方式寻找最大值是降低时间复杂度的关键,假设有n个数据,需要n-1次建堆的过程,每次建堆的时间复杂度为log2n,但是无论序列的开始状态如何,都需要对堆进行遍历寻找最大值,所以在最好情况、最坏情况和平均情况下的时间复杂度都是O(nlog2n)。
算法稳定性
我们知道堆的结构是节点i的孩子为2 * i和2 * i + 1节点,大顶堆要求父节点大于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n 的序列,堆排序的过程是从第n / 2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个元素之间的选择当然不会破坏稳定性。但当为n / 2 - 1, n / 2 - 2, … 1这些个父节点选择元素时,就会破坏稳定性。有可能第n / 2个父节点交换把后面一个元素交换过去了,而第n / 2 - 1个父节点把后面一个相同的元素没 有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,堆排序不是稳定的排序算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值