堆排序全解析

本文详细介绍了堆排序的核心——构造大顶堆,并通过两种方式展示了如何建立大顶堆。堆排序过程中,通过不断抽取堆顶元素并重新构建大顶堆实现排序。文中还解释了非叶子节点数量为数组长度的一半的原因,并提供了相应的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

构造大顶堆

第一种方式

第二种方式

堆排序就是不断抽离堆顶元素(剩余队列中最大者),继续构造大顶堆

非叶子节点有多少个,为什么是N(数组长度)/2

代码


堆排序核心在于构造堆,本文以构造大顶堆为例

说白了就是完全二叉树,

除最后一层外,其余各层都是满的,每层数量为(2^(h-1))

最后一层必须从左到右依次放置不能留空

构造大顶堆

     大顶堆,就是每个父节点都比子节点要大,即任何子树都是大顶堆

     比如给你数组,12345

  遍历所有非叶子节点都与其孩子进行比较,子节点大于父节点时,进行替换

遍历可以从下到上,也可以从上到下。

本文采用从下到上,从右到左的方式遍历,数组指针向左移动即可,每次遍历比较确保比较后的所有子树都是大顶堆。

第一种方式

第二种方式

在每次调整堆时,非叶子节点都与其孩子进行比较,子节点大于父节点时,将该非叶子节点暂存到temp,原位置为代替换区,将子节点中的大者填充至i位置,i位置移至1,i的子节点中的大者4填充至i的位置,i位置移至大的子节点4的位置,当该节点为叶子节点,把temp值放入。结果同方式一。替换的过程有点像快排。

显然第二种方式来的简单,因为减少了交换。

堆排序就是不断抽离堆顶元素(剩余队列中最大者),继续构造大顶堆

具体操作就是不断让堆顶元素与堆底元素(该堆底位置不参与下次堆排序,所以就是抽离)互换,继续构造大顶堆

编写具体代码时,思考

非叶子节点有多少个,为什么是N(数组长度)/2

N(数组长度)=N(节点数)=n0(度为0的节点)+n1(度为1的节点)+n2(度为2的节点)= 根节点 + 所有孩子节点 = 1 + n1(度为1的节点的孩子)+ 2n2(度为2的节点的孩子)

所以 n2 + 1 = n0

非叶子节点即求n1+n2为多少

因为n1的值非1即0

那么用假设法

当 n1 = 1 时, 1 + n1 + 2n2 = N    =====> 2(n1+n2) = N

当 n1 = 0时,1+ n1+ 2n2 = N =====> 1+2(n1+n2) = N ======>   n1 + n2 = (N-1) /2 因为n1为0时,n为奇数,

又因为奇数整型除法的特殊性  N /2 = (N-1)/2

所以非叶子节点有 N / 2个

代码

public class HeapSort {
	public static void main(String []args){
		int[] arr = new int[]{3,44,38,5,47,15,36,26};
		sort(arr);
	}
	public static void sort(int []arr){
		//1.构建大顶堆
		for(int i=arr.length/2-1;i>=0;i--){
			//因为是完全二叉树,所以最后一个非叶子节点之前的元素都是非叶子结点
			//从第一个非叶子结点从下至上,从右至左调整结构
			adjustHeap(arr,i,arr.length);
			SortUtil.printArray(arr);
		}
		System.out.println("part2");
		//2.调整堆结构+交换堆顶元素与末尾元素
		for(int j=arr.length-1;j>0;j--){
			swap(arr,0,j);//将堆顶元素与末尾元素进行交换
			adjustHeap(arr,0,j);//重新对堆进行调整
			SortUtil.printArray(arr);
		}
	}

	/**
	 * 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
	 * @param arr
	 * @param i
	 * @param length
	 */
	public static void adjustHeap(int []arr,int i,int length){
		int temp = arr[i];//先取出当前元素i
		for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
			if(k+1<length && arr[k]<arr[k+1]){//如果左子结点小于右子结点,k指向右子结点
				k++;
			}
			if(arr[k] >temp){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
				arr[i] = arr[k];
				i = k;
			}else{
				break;
			}
		}
		arr[i] = temp;//将temp值放到最终的位置
	}

	public static void swap(int []arr,int a ,int b){
		int temp=arr[a];
		arr[a] = arr[b];
		arr[b] = temp;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值