参考:https://www.bilibili.com/video/BV1Eb41147dK
堆的定义/条件:如果一颗完全二叉树,且满足parent ≥ children,称为大顶堆;或者parent ≤ children,称为小顶堆。
完全二叉树:逐层从上向下从左至右添加节点。
注:下面的算法只讨论大顶堆。
一、一颗完全二叉树如何构建成一个大顶堆?
用一个一维数组表示一棵完全二叉树,中间无断开,且可以拿到一个节点的父子节点的索引下标。
根据完全二叉树的性质:对于索引下标,parent = (i-1) / 2 ;c1 = 2i + 1; c2 = 2i + 2。除法取整。
从最后一个父节点开始从后向前对所有父节点做heapify操作,也即从后向前对所有的父节点做heapify。 (从最后一个父节点开始,最后一个父节点索引 = (最后一个子节点-1)/ 2 )
heapify操作 - 确保当前节点下面的分支是堆结构:
先在三个节点中取最大值,把最大值换到父节点上。
然后如果最大值所在节点不是原来根节点,交换之后,仍需对原最大值所在节点做一次heapify(递归)。
二、堆如何进行排序?
根节点肯定一定是最大的节点,
首先交换根节点与最后一个节点,把最后一个节点拎出来(相当于从树中删除最后一个节点,存到数组的最后一个位置);
然后对于新的根节点再做一次heapify(而不用做build_heap,因为除了根节点,其他分支都是大顶堆结构),使根节点仍然是当前堆得最大值,维持一个堆结构;
然后重复上述过程,直到剩下最后一个节点:根节点与最后一个节点交换,把最后一个节点拎出来,再做一次heapify。
三、代码实现:
堆排序:先建堆,再执行堆排序的过程。
public static void swap(int arr[],int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 功能:对第i个节点做heapify。参数:arr二叉树数组,n二叉树长度,i为当前节点。
public static void heapify(int arr[],int n,int i){
if(i >= n) return;
int max = i;
int child1 = 2*i+1;
int child2 = 2*i+2;
if(child1 <n && arr[max] < arr[child1]) max = child1;
if(child2 <n && arr[max] < arr[child2]) max = child2;
if(max != i){
swap(arr,i,max);
// 对上一轮max所在索引处做heapify
heapify(arr,n,max);
}
}
// 功能:将一颗完全二叉树构建成一个大顶堆
public static void build_heap(int arr[], int n){
int last_node = n-1;
//最后一个父节点索引
int parent = (last_node - 1) / 2;
//从后向前对所有的父节点做heapify
for(int i = parent; i >= 0; i--){
heapify(arr,n,i);
}
}
// 功能:给一个大顶堆排序
public static void sort_heap(int arr[], int n){
for(int i = n-1;i > 0;i--){
//交换最后一个节点和第一个节点
swap(arr,i,0);
// 对第一个节点做heapify,确保堆结构,执行heapify操作的数组长度为i,
// 即最后一个节点不参与heapify,意味着把最后一个节点去掉
heapify(arr,i,0);
}
}
public static void main(String args[]){
int[] arr = {4,7,9,1,3,3,5,2};
int n = 8;
build_heap(arr,n);
sort_heap(arr,n);
for(int i:arr){
System.out.println(i);
}
}