八种排序算法之八:两步学会堆排序

堆排序的基础是完全二叉树的顺序结构实现,没有实现过的可以先看一下这篇文章:

《二叉树链式结构和顺序结构区别详解+java代码实现》

https://blog.csdn.net/weixin_44537194/article/details/87405475

堆分为大顶堆和小顶堆,其实就是完全二叉树

大顶堆要求对于任意一个子树,父节点大于任何一个子节点
就像一个倒三角一样:
在这里插入图片描述
小顶堆相反,父节点小于任意个字节点,两者对左右孩子的大小关系不做任何要求,其实很好理解。

有了上面的定义,我们可以得知,处于最大堆的根节点的元素一定是这个堆中的最大值。

其实我们的堆排序算法就是抓住了堆的这一特点,每次都取堆顶的元素,将其放在序列最后面,然后将剩余的元素重新调整为最大堆,依次类推,最终得到排序的序列。

那么使用堆排序只需要两步:
1.把数组转化为大顶堆
2.把大顶堆元素固定在序列最后,剩余数字继续调整为大顶堆。

在这里插入图片描述
第一步:如何把数组调整为大顶堆?
先使用完全二叉树的顺序存储,把如图的数组转化为一个堆(二叉树)
从最后一个叶子节点(最后一层)向上处理,如果该子节点比他的父节点小,二者互换位置,但要注意移动后会打乱原来的排序,
也就是说需要再次比较新的节点是否比他之下的子节点小,如果小的话还需移动
在这里插入图片描述
最终完成大顶堆:
在这里插入图片描述
我们先通过代码实现这一步:

/**
 * 2019年2月16日
 */
package demo6;

import java.util.Arrays;

public class  HeapSort {
	
	public static void main(String[] args) {
		
		int[] arr =new int[]{9,6,8,7,0,1,10,4,2};
		
		//堆调整为大顶堆的方式
		//开始的位置是最后一个叶子节点的父节点
		int start = (arr.length-1)/2;
		//通过循环才可以全部查看调整
		for(int i =start;i>=0;i--){
		     maxHeap(arr,arr.length,i);
		}
		System.out.println(Arrays.toString(arr));
		
	}
	
	/**
	 * 调整为大顶堆的方法 
	 *  arr为调整的堆
	 *  length为长度 
	 *  parent为当前父节点
	 */
	public static void maxHeap(int[] arr, int length, int parent) {

		// 取出它的左右子节点,根据顺序存储的二叉树
		int leftNode = 2 * parent + 1;
		int rightNode = 2 * parent + 2;

		int max = parent;
		// 找出最大
		if (leftNode < length && arr[leftNode] > arr[max]) {
			max = leftNode;
		}
		if (rightNode < length && arr[rightNode] > arr[max]) {
			max = rightNode;
		}
		// 交换变量
		int temp;
		// 如果最大值变化,代表需要移动
		if (max != parent) {
			temp = arr[max];
			arr[max] = arr[parent];
			arr[parent] = temp;
			// 交换之后,可能会破坏原来的顺序,需要继续向下查看
			maxHeap(arr, length, max);
		}
	}

}


运行结果为:
在这里插入图片描述
与我们手动排序是一致的,验证通过。

接下来解决第二步: 把堆顶最大的元素固定到末尾,然后使用递归把剩余元素再次排成大顶堆

// 堆排序
	public static void heapSort(int[] arr) {

		// 第一步:堆调整为大顶堆的方式
		// 开始的位置是最后一个叶子节点的父节点
		int start = (arr.length - 1) / 2;
		// 通过循环才可以全部查看调整
		for (int i = start; i >= 0; i--) {
			maxHeap(arr, arr.length, i);
		}
		System.out.println("调整为大顶堆:" + Arrays.toString(arr));

		// 第二步:把堆顶固定到末尾,递归
		for (int i = arr.length - 1; i > 0; i--) {
			// 交换,固定最大数
			int x = arr[0];
			arr[0] = arr[i];
			arr[i] = x;
			// 递归,把剩余重新排序
			// 父节点从零开始
			maxHeap(arr, i, 0);
		}
	}

这里有一个血泪史:在第二步递归的时候,我们调用 “ 调整大顶堆的 ”方法,但是我把参数顺序写错了,一直得不到正确结果,强调一下
第一个参数是待调整数组,
第二个是调整的长度,因为数组尾部已调整好的元素冗余,长度不断减小
第三个时开始的父节点索引
在这里插入图片描述

排序结果及全部代码如下:

在这里插入图片描述

package demo6;

import java.util.Arrays;

/**
 * 堆排序
 *
 * 2019年2月16日
 */
public class HeapSort {
	
	public static void main(String[] args) {

		int[] arr = new int[] { 9, 6, 8, 7, 0, 1, 10, 4, 2 };
		heapSort(arr);

		System.out.println("堆排序结果:    " + Arrays.toString(arr));

	}

	// 堆排序
	public static void heapSort(int[] arr) {

		// 第一步:堆调整为大顶堆的方式
		// 开始的位置是最后一个叶子节点的父节点
		int start = (arr.length - 1) / 2;
		// 通过循环才可以全部查看调整
		for (int i = start; i >= 0; i--) {
			maxHeap(arr, arr.length, i);
		}
		System.out.println("调整为大顶堆:" + Arrays.toString(arr));

		// 第二步:把堆顶固定到末尾,递归
		for (int i = arr.length - 1; i > 0; i--) {
			// 交换,固定最大数
			int x = arr[0];
			arr[0] = arr[i];
			arr[i] = x;
			// 递归,把剩余重新排序
			// 父节点从零开始
			maxHeap(arr, i, 0);
		}
	}

	
	/**
	 * 调整为大顶堆的方法 
	 *  arr为调整的堆
	 *  length为长度 
	 *  parent为当前父节点
	 */
	public static void maxHeap(int[] arr, int length, int parent) {

		// 取出它的左右子节点,根据顺序存储的二叉树
		int leftNode = 2 * parent + 1;
		int rightNode = 2 * parent + 2;

		int max = parent;
		// 找出最大
		if (leftNode < length && arr[leftNode] > arr[max]) {
			max = leftNode;
		}
		if (rightNode < length && arr[rightNode] > arr[max]) {
			max = rightNode;
		}
		// 交换变量
		int temp;
		// 如果最大值变化,代表需要移动
		if (max != parent) {
			temp = arr[max];
			arr[max] = arr[parent];
			arr[parent] = temp;
			// 交换之后,可能会破坏原来的顺序,需要继续向下查看
			maxHeap(arr, length, max);
		}
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值