目录
1、二叉堆
二叉堆是一种特殊的堆,二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
二叉堆一般用数组来表示。如果根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1。因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。
下图中a[1]的子结点就是a[2]和a[3].
2、堆排序
堆排序就是利用了二叉堆中的最大堆。即每个父结点(数组中下标k),都要大于等于它的子结点(数组中下标2k和2k+1)。
堆排序的步骤:1、对于一个无序数组a,先将它构建成最大堆。这时候二叉堆的根节点就是数组中最大的那个元素。
2、然后我们把最大的根节点拿出来放在该数组中末尾。
3、将剩下的元素再构建成最大堆,得到第二大元素,放在第一大元素的前面。
4、不断循环。。。。。。。。。。。。。。。。类似冒泡排序的思想,每次先把最大的元素拿出来放在末尾。
3、代码
public class Heap {
public static void sort(double[] a){
/**
* 构建二叉堆的最大堆
* 为什么从N/2的位置开始构建?
* 因为 N/2的子结点就是N,在构建二叉堆的时候,我们会将父结点和子结点比较,
* 如果子结点比父结点大,那么我们会将子结点换到父结点的位置,如果从比N/2大
* 的地方进行构建,后面就没有子结点可以和它比较了。
*/
int N=a.length;
for (int k=N/2;k>=1;--k){
sink(a,k,N);
}
//不断拿出二叉堆的最大元素,放在数组的末尾,然后将剩下的元素构建成最大堆
while(N>1){
exch(a,1,N--);
sink(a,1,N);
}
}
/**
* 将下标为k的元素,不断和它子结点(2k)比较
* 如果比自己子结点小,就把子结点换上来,否则就变成了最大堆(父结点大于等于子结点)
*/
private static void sink(double[] a, int k, int N ) {
while (2*k<N){
int j=2*k;
if (j<N&&less(a,j,j+1)) j++;
if (!less(a,k,j)) break;
exch(a,k,j);
k=j;
}
}
/**
* 为什么都要减一?
* 是因为我们定的二叉树的根节点在a[1]的位置,
* 实际数组的根节点在a[0]
*/
private static boolean less(double[] a,int i,int j){
return a[i-1]<a[j-1];
}
private static void exch(double[] a, int i, int j) {
double temp=a[i-1];
a[i-1]=a[j-1];
a[j-1]=temp;
}
private static void show(double[] a) {
System.out.println("\n");
for (double item : a) {
System.out.print((int) item + ",");
}
}
public static void main(String[] args) {
double[] a = { 55, 43, 23, 12, 13, 11, 7, 8, 88, 6, 3, 2, 4, 1, 9, 8, 7, 11, 56, 45, 22, 23,
45, 66 };
sort(a);
show(a);
}
}
4、应用场景
堆排序在排序复杂性的研究中有着重要的地位, 因为它是我们所知的唯一能够同时最优地利用空间和时间的方法——在最坏的情况下它也能保证使 用~2NlgN次比较和恒定的额外空间。
当空间十分紧张的时候(例如在嵌入式系统或低成本的移动设 备中)它很流行,因为它只用几行就能实现(甚至机器码也是)较好的性能。但现代系统的许多应用 很少使用它,因为它无法利用缓存。数组元素很少和相邻的其他元素进行比较,因此缓存未命中的次 数要远远高于大多数比较都在相邻元素间进行的算法,如快速排序、归并排序,甚至是希尔排序。
来源:CSDN
作者:我要看一下山顶的风景
链接:https://blog.csdn.net/qq_34589749/article/details/104088287