堆排序

前言

     本章总结堆排序。-_-

    

☆☆☆ 算法目录导航页,包含基础算法、高级算法、机器学习算法等☆☆☆

    

1.堆的定义

堆实际上是一棵完全二叉树,满足2个性质:
    1.堆的每一个父节点都大于(或小于)其子节点;
    2.堆的每个左子树和右子树也是一个堆。
堆分为两类:
    1、最大堆(大顶堆):堆的每个父节点都大于其孩子节点;
    2、最小堆(小顶堆):堆的每个父节点都小于其孩子节点;

在这里插入图片描述

2.堆排序过程

  堆排序过程分3块:
    1.初始化堆,即调整为堆;
    2.交换堆顶和最后一个节点值,然后输出最后一个节点;
    3.将剩余的节点继续调整为堆。
    注意:交换完成后,要考虑子节点及其子节点所在树的堆结构是否发生变化。
    小顶堆为例,画了个简易图如下:

在这里插入图片描述

3.小顶堆代码示例

import java.util.Arrays;

/**
 * 
 * @author wxq
 *
 */
public class SortHeap {
	/**
	 * 测试
	 * @param args
	 */
	public static void main(String[] args) {
		int [] arr = new int [] {1,8,6,4,2,3,5};
		heapSort(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	/**
	 * 
	 * 调整堆,只考虑3个节点【时刻记住】。
	 * 完全二叉树特性:可用数组表示左右子树
	 * 关键点:左子树 left = 2 * root +1 
	 * 		     右子树 right= left + 1 
	 * 小顶堆为例
	 */
	static void adjustHeap(int [] arr,int size,int root){
		int left = 2 * root +1 ;//左子节点
		int right = left + 1;	//右子节点
		int min_index = root;//假设最小值索引为当前root节点
		
		//左子节点值小于最小值
		if(left < size && arr[left] < arr[min_index]){
			min_index = left;
		}
		//右子节点值小于最小值
		if(right < size && arr[right] < arr[min_index]){
			min_index = right;
		}
		//如果假设不成立,说明root不是最小,则需要交换。因为小顶堆需要满足节点值都不大于它的左右子节点值。
		if(min_index != root){
			swap(arr, min_index, root);
			//交换完成后,需要调整左右子节点及其叶子节点
			adjustHeap(arr, size, min_index);
		}
	}
	
	/**
	 * 堆排
	 * @param arr
	 */
	static void heapSort(int [] arr){
		int size = arr.length;
		//最后一个非叶子节点
		int last = size/2 - 1;
		//1.建堆
		for(int i = last; i >= 0;i--){
			adjustHeap(arr, size, i);
		}
		
		//2.输出(swap)最小值后,再将剩余元素调整为堆
		while(size > 1){			//剩余一个数就不需要交换了
			swap(arr, 0, size -1);	//交换后最后一个节点即为最小值
			size--;					//输出最小值,此时只剩size-1个数需要调整
			adjustHeap(arr, size, 0);//剩余元素继续调整,从堆顶开始
		}
	}
	
	
	/**
	 * 交换
	 * @param arr
	 * @param a
	 * @param b
	 */
	static void swap(int[] arr, int a, int b) {
		int temp = arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}
}

4.大顶堆代码示例

/**
	 * 当前节点为k (数组表示完全二叉树时,k为下标):
	 * 父节点:(k -1) / 2
	 * 左孩子:2 * k + 1
	 * 右孩子:2 * k + 2
	 * @param arr
	 * @param curRoot 当前筛选的节点,【【【【注意:筛选只考虑非叶子节点!!!!!
	 * @param size 数组大小
	 */
	private static void adjustHeap(int[] arr, int curRoot, int size) {
		int left = 2 * curRoot + 1;
		int right= 2 * curRoot + 2;
		int max_index = curRoot;//假设当前非叶子节点为当前子树的最大值
		
		//发生交换之前,只考虑当前子树(3个节点的大小比较)
		if(left < size && arr[left] > arr[max_index]){
			max_index = left;
		}
		if(right < size && arr[right] > arr[max_index]){
			max_index = right;
		}
		
		//若当前节点不是最大,就交换,交换后就要调整
		if(max_index != curRoot){
			//只要发生了交换,就要调整堆
			swap(arr, max_index, curRoot);
			adjustHeap(arr, max_index, size);
		}
	}

5.小顶堆其他代码示例(不好理解)

/**
	 * 小顶堆,非递归,不好理解~
	 */
	static void adjustDown_min(int arr[], int root, int size)
	{
	    int left = root * 2 + 1;//左孩子
	    int right = left + 1;	//右孩子
	    while (left < size)
	    {
	        if (right<size && arr[left] > arr[right])//子节点中找较小的
	        {
	        	left++;
	        }
	        if (arr[root] < arr[left])
	        {
	            break;
	        }
	        swap(arr, left, root);
	        root = left;
	        left = root * 2 + 1;
	    }
	}

6.输出结果

在这里插入图片描述

7.堆应用:Top K问题

    略。版本较多,有需要的话再列举。

总结:

     本文讲了小顶堆排序,大顶堆原理一样,只不过堆顶为最大值。堆排时间复杂度O(nlog2n),空间复杂度O(1),不稳定。需要注意的是:如何将数组抽象为完全二叉树,即那几个公式,如果理解不了就背过,也没啥,就是一种规律而已。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值