堆,物理上看是普通的数组,但是逻辑上是二叉树。它的最大应用是,在海量数据中寻找Top K。
如果是找数据中最大的K个元素、第K大的元素(使用小根堆)。
如果是找数据中最小的K个元素、第K小的元素(使用大根堆)。
注意:
1、堆的一个特点是原地操作,所以不要申请新的空间来存堆,原地操作即可。
2、如果不想自己实现堆,可以用STL-multiset来当作堆使用。
堆的用法:
这里是把堆作为一种存储数据的载体(和堆排序是不一样的事儿,堆排序只是堆的一种应用场景)。
宏观上看,堆要实现的接口操作一般为:
1、查询堆顶元素
2、向堆中插入新的元素
3、从堆中弹出堆顶元素
4、更新堆顶元素
微观上看,堆是如何实现这三个接口的:
1、堆顶元素就是数组的首个元素。
2、向队尾插入新元素,然后由新元素叶子向根节点方向调整。(堆的初始状态是空的)
3、取得堆顶元素,将堆尾元素替换过来,然后sink操作。
4、更新栈顶,然后从堆顶向下调整,sink操作。
数据结构:
一个数组int A[ ] , 一个当前堆大小int n
(注意:为了运算方便,A[0]空着不用,数组的空间确保足够使用)
三个操作(实现的是小根堆):
1、返回堆顶元素
A[1]就是堆顶元素。
2、向堆插入新元素 : 参数为(堆,堆大小,插入值)
void minHeapPush(int A[], int &n, int value)
{
n++;
A[n] = value;
int i = n, p = n/2;
while(p >= 1 && A[p] > A[i])
{
swap(A[p], A[i]);
i = p;
p = p/2;
}
}
3、弹出堆顶元素 : 参数(堆,堆大小)
int minHeapPop(int A[], int &n)
{
int top = A[1];
swap(A[1], A[n]);
n- - ;
//下面开始sink()
int i, lc, rc, minc;
i=1;
lc = i*2;
while(lc <= n)
{
rc = lc + 1;
minc = (rc > n)?lc:((A[lc]<A[rc])?lc:rc); //minc为较小的孩子
if(A[minc] >= A[i])
break;
i = minc;
lc = 2*i;
}
return top;
}
4、更新堆顶元素 :参数为(堆,堆大小,堆顶更新值)
void minHeapSetTop(int A[], int n, int value)
{
A[1] = value;
//下面也开始sink()
int i, lc, rc, minc;
i = 1;
lc = i*2;
while(lc <= n)
{
rc = lc + 1;
minc = (rc > n)?lc : ((A[lc]<A[rc])?lc:rc);
if(A[minc] >= A[i])
return;
i = minc;
lc = 2*i;
}
}
PS:以后用上述代码实现堆之后,就可以直接用于适合的场景。