堆排序算法

1.介绍:

堆排序(Heapsort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种,它的最坏,最好,平均时间复杂度均为O(nlogn)。可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

2.什么是堆:

堆具有以下性质:

  • 它是完全二叉树:
  • 每个结点的值都大于或等于(小于或者等于)其左右孩子结点的值。


对堆中的节点进行从上到下,从左到右进行编号,对应着数组中的每个元素的下标。以大顶堆为例,堆中的逻辑结构对应着数组中的存储结构如下所示:


所以对于堆中的父子节点之间对应关系为:

大顶堆:arr[ i ] >= arr[ 2 * i + 1] 并且 arr[ i ] >= arr[ 2 * i + 2] ,其中  0 <= i <= arr.length - 1

小顶堆:arr[ i ] <= arr[ 2 * i + 1] 并且 arr[ i ] <= arr[ 2 * i + 2] ,其中  0 <= i <= arr.length - 1

其中,最后一个非叶子节点的编号(下标)为:arr.length / 2 - 1

3.堆排序基本思想:

这里我们以大顶堆来举例,小顶堆的思想类似。一共分为3步:

1. 将待排序数组创建(调整)成一个大顶堆,此时,堆顶元素是数组的最大值。

2. 将堆顶元素与末尾元素进行交换,此时末尾元素为最大值。

3. 将剩余的元素[0.....n-1]重新调整成一个大顶堆,现在问题就转换为了对这n-1个元素进行堆排序了。反复执行以上步骤,直到全部元素都有序。

下面我们以大顶堆为例,来演示堆排序的过程:

初始状态:


创建堆:

我们的目的是使得堆顶为最大的元素。首先,叶子节点满足堆的性质,我们不需要调整。从最后一个非叶子节点开始,它的下标为:arr.length / 2 - 1,即上图中的下标为2,元素值为1的节点,我们令当前调整的元素为节点i,让当前的节点与左右孩子节点进行比较,取左右孩子节点的最大值。(1)如果孩子节点的最大值不比节点i大,那么就不交换,继续调整节点i-1;(2)如果孩子节点的最大值比当前节点要大,那么就交换。交换以后,被交换的孩子节点可能不满足堆的性质,所以我们需要继续对孩子节点进行同样的操作,直到节点i对应的分支全部满足堆结构。节点i开始,每次i = i - 1 ,一直到堆顶节点。这样当所有的非叶子节点都调整为满足堆结构以后,堆顶元素就是最大的。

步骤1:


步骤2:


步骤3:


步骤4:

由于步骤3中12和8的交换导致孩子节点(节点值为8)分支不满足堆结构,所以需要调整孩子节点。


至此:大顶堆就创建好了,堆顶元素为最大的,接下来把堆顶元素和末尾元素进行交换。

首尾交换:


此时尾部元素12已经有序,并且为最大的,可以把它砍掉,我们继续对剩下的n-1个元素进行同样的操作就可以了。把1放在堆顶后,目前的结构不满足堆的性质,需要对堆顶元素按照上面的步骤进行调整,直到数组中的所有元素有序为止。

4.代码演示(Java版):

import java.util.Arrays;
public class MaxHeap {
	public static void main(String[] args) {
		int[] arr = new int[]{8,6,1,12,10,7};
		sortHeap(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	/**
	 * 堆排序
	 * @param arr
	 */
	public static void sortHeap(int[] arr){
		//构建最大堆
		for(int i = arr.length / 2 - 1; i >= 0; i--){
			adjustHeap(arr, i, arr.length);
		}
		//首位交换,并重新调整堆结构
		for(int j = arr.length - 1; j >= 0; j--){
			//交换
			int temp = arr[j];
			arr[j] = arr[0];
			arr[0] = temp;
			//继续调整
			adjustHeap(arr, 0, j);
		}
	}
	
	/**
	 * 调整最大堆
	 * @param arr 原数组
	 * @param i   当前需要调整的节点的编号 
	 * @param length 剩余节点的个数
	 */
	public static void adjustHeap(int[] arr,int i,int length){
		int temp = arr[i];//当前节点的值
		for(int k = 2 * i + 1; k < length; k = 2 * k + 1){
			//获取当前节点的孩子节点的最大值
			if(k + 1 < length && arr[k + 1] > arr[k]){
				k = k + 1;
			}
			//如果孩子节点更大,则把最大的孩子节点的值赋值给父节点
			if(arr[k] > temp){
				arr[i] = arr[k];
				i = k;
			}
			else break; //说明当前节点往下都不需要调整了
		}
		arr[i] = temp;
	}
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值