最小堆的定义
一般情况下,最小堆是指:父亲节点堆值大于其所有子节点堆值;
下面来通过举例来说明最小堆:
设有数组:{75,96,2,7,102,81,43,27,96,112,704}
建立最小堆
下述中用到的交换函数swap
private static void swap(int i, int j, int[] data) {
int te = data[i];
data[i] = data[j];
data[j] = te;
}
对于数组来建立最小堆
//此处N=data.length;data为要处理数组
buildHeap(data,N);
private static void buildHeap(int[] data, int N) {
for (int i=N/2-1;i>=0;i--){
adjust(data,i,N);
}
}
建立堆后,满足上述最小堆堆条件,需要堆进行调整:
堆的调整从上忘下进行
此处按照第一步,第二步,第三步处理;(第一步选取的位置为最后一个叶子节点存在的堆,公式为:data.length/2-1)
//调整的代码为,arr为目标数组,i为父节点位置,n为堆的大小
private static void adjust(int[] arr, int i, int n) {
int j = i * 2 + 1;//子节点
while (j<n)
{
if (j+1<n && arr[j] > arr[j + 1]) {//因为是最小堆的调整,所以找到堆中子节点的最小值
j++;
}
if (arr[i] <arr[j]) {//将此最小值与父节点进行比较,如果父节点小,则结束,否则,将节点的值进行交换
break;
}
swap(i,j,arr);//调整索引中的值,将两值进行交换
i = j;修改父节点位置
j = i * 2 + 1;//针对该节点下的数据进行调整
}
}
调整完后可认为,该堆已经是一个最小堆,如图:
至此最小堆的建立与调整完成;
最小堆排序
对于上图,我们可以看出,调整完后,最小堆也是无序的,如上式其数组表示为:
{2,7,43,27,102,81,75,96,96,112,704}
针对此堆进行排序;
我们发现,最小堆的根节点的数一直是最小的:因此,我们可以对此线现象找出调整
- 将第一个数与最后一个数进行调整
- 然后将最小堆中除了最后一个数外的堆进行调整
- 调整完堆最小堆中,跟节点是除了最后一个数外最小堆数
- 重复1~3过程,直到排除到堆外的数为总数减1,则可认为此时已经调整完毕
//堆排序代码如下所示
private static void HeapSort(int[] data, int n) {
for(int i = n-1;i>0;i--){
swap(i,0,data);
adjust(data,0,i);
}
}
调整完毕后,可得到倒序到数组数据
{704, 112, 102, 96, 96, 81, 75, 43, 27, 7, 2}
用图形表示,即为
至此对排序完成。
TopK问题
如上述问题,我们得到排序。但是有时我们会遇到一些求数组中前K大的数据。此类问题被称为TopK问题
针对上述问题,假设:{75,96,2,7,102,81,43,27,96,112,704},求出最大的五个数(或者倒数第5大的数)
- 还是像第一部分一样建立一个堆中元素个数为K=5的堆并调整堆
- 将第六个元素(后续数组)中的数与堆的根节点(数组中第一个位置)元素进行比较,若比第一个元素小,则说明,他不在前五大的数里面,直接排除,进行下一个元素的比较。
- 重复上述整个过程完成后续所有元素对堆中数堆调整
代码为:
//此处的N为你想求出的前N个数
private static void findTop(int[] data, int N) {
for(int i=N;i<data.length;i++){
if(data[i]>data[0]){
swap(i, 0,data);
adjust(data, 0,N);
}
}
}
将数组后续堆数**{81,43,27,96,112,704}**,依次与根节点比较,来寻找比根节点大的数。
重复此过程,将求出
自此可以求出前K大,或者第K大的数。
大根堆与小根堆
大根堆与小根堆代码的区别:
1.建立并调整的堆中,区别为
小根堆中
大根堆中
2.TopK问题中中,区别为
小根堆
大根堆
总结
- 小根堆可以求出TopK问题。
- 可推出,大根堆可以求出前几个最小值的问题问题。
- 小根堆可以排序,但是倒序
- 可推出:大根堆也可以排序,但是是正序
- 堆排序的时间复杂度为O(nlogn)