堆
0.简介:
堆是一种神奇的数据结构,它可以支持以下几种操作:
-
- 查询最大(最小)的元素
- 删除最大(最小)的元素
- 插入一个元素
例如,这就是一个小根堆
其实堆就是一棵满足任意一个子节点都不大于(或不小于)其父节点的完全二叉树。
1.基本操作:
- 插入
现在我们要插入一个新的元素8,那就先把它排到堆尾
然后我们把它上调直至满足堆的性质。先比较它的父节点,发现8<10,那么就把它与它的父节点交换。
再比较此时的父节点,发现8>=1,无法再向上调整。break退出循环。
于是堆最终变成了这样:
- 弹出
我们假设不想要这个1了,该怎么办呢?肯定是不能直接将它删除的。因为那样会破坏堆的性质。
我们可以考虑这样:先把堆顶与堆尾交换,然后删除堆尾。
再逐步把此时的堆顶(即原来的堆尾)向下调整至合适的位置,使得此时的堆满足堆的性质。
此时我们发现,5<10,但是8也<10啊!那怎么办呢?
我们应先比较子节点的大小,选择小的那个与之交换。为什么?
试想我们若把10与8交换,会出现问题:此时8与5不满足堆的性质了!我们该如何调整呢?
此时我们就应该选择小的那个,这样就没有问题了。
此时我们再比较此时节点与其子节点的大小,发现较小的那个都要比此时节点要大,那么向下调整至此结束。break跳出循环。
-
- 查询最大(最小)元素
这个就较为简单了。由于我们前面两种操作已经维护了堆的基本性质,我们现在返回堆顶元素即可。
2.代码实现
1 #include <cstdio> 2 #include <cstring> 3 #define maxn 1000010 4 #define rgi register int 5 #define il inline 6 #define swap(a,b) a^=b^=a^=b 7 8 using namespace std; 9 10 int n, siz; 11 int h[maxn];//小根堆 12 13 il void push(int val) 14 { 15 h[++siz] = val;//往队尾添加元素 16 int cur = siz;//cur表示当前要向上调整的节点编号,初始化为队尾编号 17 while(cur >= 1)//当cur不是堆顶的时候循环 18 { 19 if(h[cur]<h[cur >> 1])//如果自己的父亲比自己大 20 swap(h[cur], h[cur >> 1]);//交换 21 else 22 break;//如果无法交换,则表明已经调整完毕 23 cur >>= 1;//开始处理自己的父亲节点 24 } 25 } 26 27 il void pop() 28 { 29 swap(h[1], h[siz]);//交换队首与队尾元素 30 h[siz--] = 0;//删除 31 int cur = 1;//cur表示当前要向下调整的节点编号,初始化为队首编号 32 while((cur << 1) <= siz) 33 { 34 int lc = cur << 1, rc = lc + 1, _next; 35 //lc是左儿子编号,rc是右儿子编号,_next是下一步要处理的节点编号(要么是左儿子,要么是右儿子) 36 if(rc <= siz)//如果rc没有超siz,就比较lc与rc的大小,交换小的那一个 37 { 38 if(h[lc] <= h[rc]) 39 _next = lc; 40 else 41 _next = rc; 42 } 43 else//如果rc超了siz,那么只能考虑左儿子了 44 _next = lc; 45 if(h[_next] < h[cur])//如果下一步要处理的节点比当前节点小 46 { 47 swap(h[_next], h[cur]);//交换 48 cur = _next;//开始处理下一个节点 49 } 50 else 51 break;//如果无法交换,则表明已经调整完毕 52 } 53 } 54 55 il int top() 56 { 57 return h[1];//堆顶自然是h[1] 58 } 59 60 int main() 61 { 62 scanf("%d", &n); 63 for(rgi i = 1; i <= n; i++) 64 { 65 int op; 66 scanf("%d", &op); 67 if(op == 1) 68 { 69 int v; 70 scanf("%d", &v); 71 push(v); 72 } 73 else if(op == 2) 74 printf("%d\n", top()); 75 else if(op == 3) 76 pop(); 77 } 78 return 0; 79 } 80
3.应用
洛谷P1090 合并果子
考虑维护一个小根堆,每次弹出第一大与第二大的,然后合并(即加起来)之后再插入堆中。
正确性不难证明:设有a<b<c,我们先考虑从小到大进行合并,则:
$s_{1} = (a+b)+(c+a+b)=2a+2b+c $
而其他的方法,如:
$s_{2} = (b+c)+(a+b+c)=a+2b+2c $
则一定有$s_{1} \leq s_{2}$
洛谷P2085 最小函数值
考虑不断维护一个小根堆,每次弹出最小的$ f(x) $,并将$ f(x+1) $ 压入堆里
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cctype> 5 #define rgi register int 6 #define il inline 7 #define ll long long 8 #define maxn 10010 9 10 using namespace std; 11 12 int n, m, siz; 13 int a[maxn], b[maxn], c[maxn]; 14 15 il int read() 16 { 17 rgi x = 0, f = 0, ch; 18 while(!isdigit(ch = getchar())) f |= ch == '-'; 19 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); 20 return f ? -x : x; 21 } 22 23 il void write(int x) 24 { 25 if(x < 0) putchar('-'), x = -x; 26 if(x > 9) write(x / 10); 27 putchar(x % 10 + 48); 28 } 29 30 struct node 31 { 32 int a, b, c; 33 int val, x; 34 } h[maxn]; 35 36 int f(struct node cur) 37 { 38 return cur.a * cur.x * cur.x + cur.b * cur.x + cur.c; 39 }//求f(x) 40 41 struct node top() 42 { 43 return h[1]; 44 } 45 46 void push(struct node now) 47 { 48 h[++siz].val = now.val; 49 h[siz].x = now.x; 50 h[siz].a = now.a; 51 h[siz].b = now.b; 52 h[siz].c = now.c; 53 int cur = siz; 54 while(cur >= 1) 55 { 56 if(h[cur].val < h[cur >> 1].val) 57 swap(h[cur >> 1], h[cur]); 58 else 59 break; 60 cur >>= 1; 61 } 62 } 63 64 void pop() 65 { 66 swap(h[1], h[siz--]); 67 int cur = 1; 68 while((cur << 1) <= siz) 69 { 70 int lc = cur << 1, rc = lc + 1, nxt; 71 if(rc <= siz) 72 { 73 if(h[lc].val <= h[rc].val) 74 nxt = lc; 75 else 76 nxt = rc; 77 } 78 else 79 nxt = lc; 80 if(h[nxt].val < h[cur].val) 81 { 82 swap(h[nxt], h[cur]); 83 cur = nxt; 84 } 85 else 86 break; 87 } 88 } 89 90 int main() 91 { 92 n = read(), m = read(); 93 for(rgi i = 1; i <= n; ++i) 94 { 95 struct node temp; 96 temp.a = read(); 97 temp.b = read(); 98 temp.c = read(); 99 temp.x = 1; 100 temp.val = f(temp); 101 push(temp);//初始化 102 } 103 for(rgi i = 1; i <= m; ++i) 104 { 105 struct node cur = top(); 106 pop(); 107 write(cur.val), putchar(' ');//提取堆顶的f(x)并输出 108 cur.x++; 109 cur.val = f(cur); 110 push(cur);//f(x+1)入堆 111 } 112 return 0; 113 }
posted on 2019-03-09 21:30 Clever_Jimmy 阅读(258) 评论(0) 编辑 收藏 举报