二叉堆 学习笔记

概述

堆是一种数据结构。堆的形态是一棵树,满足每个节点的权值都大于等于或小于等于其父亲的权值,分别称为小根堆、大根堆。一般支持插入、删除最值、查询最值等操作。

下文以小根堆为例。

二叉堆

二叉堆是完全二叉树,同时满足堆的性质,不可并(复杂度过高)。

查询最值

返回堆顶即可。

T top(){
  return h[1];
}

上浮、下沉

对于单独的一个破坏堆的性质的节点,将其不断向上交换或向下交换。

如果节点在当前位置过小,那么不断与父节点交换,直到大于等于父节点或成为根节点。

如果过大,就不断与叶节点中更小的交换,直到比子节点小或成为叶节点。

void pushup(int pos){
  for(;pos>1&&h[pos]<h[pos>>1];pos>>=1)swap(h[pos],h[pos>>1]);
}
int son(int pos){
  return (h[pos<<1]<h[pos<<1|1]||(pos<<1)==s)?(pos<<1):(pos<<1|1);
}
void pushdown(int pos){
  for(int t=son(pos);t<=s&&h[t]<h[pos];pos=t,t=son(pos))swap(h[pos],h[t]);
}

正确性证明:

如图,将 \(6\) 向上调整,而路径上其他节点都下沉。\(1\) 号的新儿子是 \(4\)\(3\) 号的新儿子是 \(6\)。而没有调整前 \(1,3\) 分别是 \(4,6\) 的二级祖先,显然不会破坏堆的性质。下沉类似。

插入

先放到堆底,然后向上调整。

void push(T x){
  h[++s]=x,pushup(s);
}

删除

把堆顶和堆底交换,向下调整堆底。

void pop(){
  swap(h[1],h[s--]),pushdown(1);
}

小技巧

STL

如果只需要用上面几种简单的操作,没有必要手写,使用 STL 的 priority_queue 即可。priority_queue<int> 是大根堆,priority_queue<int,vector<int>,greater<int>> 是小根堆。

对顶堆

对顶堆是一个大根堆、一个小根堆的组合,可以动态维护第 \(k\) 大值。

用小根堆存前 \(k\) 大值,大根堆存剩下的值。显然第 \(k\) 大就是小根堆顶。

插入元素时与小根堆顶比较,大则插入小根堆。删除时直接删除小根堆顶,然后调整堆。

当小根堆堆的元素个数不为 \(k\) 时,取出元素多的堆中的堆顶放到另一个堆中。

维护第 \(k\) 小同理,大根堆存前 \(k\) 小值,小根堆存剩下的值。

P1801

对顶堆例题,按题意模拟即可。

由于插入后会立即调整,不用与小根堆顶比较。

#include<bits/stdc++.h>
using namespace std;
int m,n,a[200005],u[200005],p;
priority_queue<int>q1;
priority_queue<int,vector<int>,greater<int>>q2;
int main(){
  cin>>m>>n;
  for(int i=1;i<=m;i++)cin>>a[i];
  for(int i=1;i<=n;i++)cin>>u[i];
  for(int i=1;i<=n;i++){
    while(p<u[i])q1.push(a[++p]),q2.push(q1.top()),q1.pop();
    cout<<q2.top()<<'\n',q1.push(q2.top()),q2.pop();
  }
  return 0;
}

[[数据结构]]

posted @ 2024-03-01 09:29  lgh_2009  阅读(9)  评论(0)    收藏  举报