一维数组模拟堆

1.
复制代码
 1 /如何手写一个堆?完全二叉树 5个操作
 2 //1. 插入一个数         heap[ ++ size] = x; up(size);
 3 //2. 求集合中的最小值   heap[1]
 4 //3. 删除最小值         heap[1] = heap[size]; size -- ;down(1);
 5 //4. 删除任意一个元素   heap[k] = heap[size]; size -- ;up(k); down(k);
 6 //5. 修改任意一个元素   heap[k] = x; up(k); down(k);
 7 #include <iostream>
 8 
 9 using namespace std;
10 
11 int const N = 100010;
12 
13 //h[i] 表示第i个结点存储的值,i从1开始,2*i是左子节点,2*i + 1是右子节点
14 //size 既表示堆里存储的元素个数,又表示最后一个结点的下标
15 int h[N], siz; //堆有两个变量h[N],size; 怎么这里的size和文件里有冲突,只能改成siz了
16 
17 void down(int u)
18 {
19     int t = u;//t存储三个结点中存在的最小的结点的下标,初始化为当前结点u
20     if (u * 2 <= siz && h[u * 2] < h[t]) t = u * 2; // 左子节点存在并且小于当前结点,更新t的下标
21     if (u * 2 + 1 <= siz && h[u * 2 + 1] < h[t]) t = u * 2 + 1;//右子节点存在并且小于当前结点,更新t的下标
22     if (t != u)//如果t==u意味着不用变动,u就是三个结点中拥有最小值的结点下标,否则交换数值
23     {
24         swap(h[t], h[u]);
25         down(t); //交换数值后,t这个结点存储原本u的值,u存储存储t的值(三个数中的最小值)。u不用调整了,但t情况不明,可能需要调整。直到它比左右子节点都小
26     }
27 }
28 
29 int main()
30 {
31     int n, m;
32     cin >> n >> m;
33     for (int i = 1; i <= n; i ++ ) scanf("%d", &h[i]); 
34     siz = n; //初始化size,表示堆里有n 个元素
35 
36     for (int i = n / 2; i; i --) down(i); //把堆初始化成小根堆,从二叉树的倒数第二行开始,把数字大的下沉
37 
38     while (m -- )
39     {
40         printf("%d ", h[1]);
41         h[1] = h[siz];
42         siz --;
43         down(1);
44     }
45 
46     return 0;
47 }
View Code
复制代码

 2.acwing 839

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 100010;
 5 int n,m;
 6 int h[N],siz; //模拟堆以及堆中有多少个元素
 7 int ph[N],hp[N]; //第k个插入的点是哪个点(下标); 堆里面某个点是哪个插入的点
 8 //ph从下标映射到堆里面去 ,hp从堆里映射到下标 
 9 
10 void heap_swap(int a, int b)
11 {
12     swap(ph[hp[a]],ph[hp[b]]);
13     swap(hp[a],hp[b]);
14     swap(h[a],h[b]);
15 }
16 
17 void down(int u)
18 {
19     int t = u; //t来表示三个点中的最小值的编号
20     if(u * 2 <= siz && h[u * 2] < h[t]) t = u * 2; //左儿子存在且左儿子小于t ,t等于左儿子
21     if(u * 2 + 1 <= siz && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
22     if(u != t) //堆顶不是最小值
23     {
24         heap_swap(u,t);
25         
26         down(t);
27     }
28 }
29 
30 void up(int u)
31 {
32     while(u / 2 > 0 && h[u / 2] > h[u])
33     {
34         heap_swap(u / 2,u);
35         u /= 2;
36     }
37 }
38 
39 int main()
40 {
41     int n,m = 0;
42     scanf("%d", &n);
43     
44     while (n -- )
45     {
46         char op[10];
47         int k ,x;
48         
49         scanf("%s", op);
50         if(!strcmp(op,"I"))
51         {
52             scanf("%d", &x);
53             siz ++;
54             m ++;
55             ph[m] = siz,hp[siz] = m;
56             h[siz] = x;
57             up(siz);
58         }
59         else if(!strcmp(op,"PM"))
60         {
61             printf("%d\n",h[1]);
62         }
63         else if(!strcmp(op,"DM")) 
64         {
65             heap_swap(1,siz);
66             siz --;
67             down(1);
68         }
69         else if(!strcmp(op,"D"))
70         {
71             scanf("%d", &k);
72             k = ph[k]; //找到堆中间的位置
73             heap_swap(k,siz);
74             siz --;
75             down(k),up(k);
76             
77         }
78         else {
79             scanf("%d%d", &k, &x);
80             k = ph[k];
81             h[k] = x;
82             down(k),up(k);
83         }
84     }
85     return 0;
86 }
Code
复制代码

 3.合并果子P1090 [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

 

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10000+10;
 4 int n,heap[maxn],siz = 0;
 5 void up(int u) //向上修复小根堆的过程
 6 {
 7     if( u > 1 &&h[u / 2] > h[u])
 8     {
 9         swap(h[u],h[u / 2]);
10         u /= 2;
11     }
12     //cout << " " << u;
13     
14 }
15 
16 void insert(int x) //二叉堆插入,新元素放在堆底,向上调整
17 {
18   heap[++siz] = x;
19   up(siz);
20 }
21 void down(int u) //二叉小根堆向下调整
22 {
23   int t = u * 2;
24   while(t <= siz)
25   { //下面这句话是从左右儿子中选一个更小的做交换
26     if(t < siz && heap[t + 1] < heap[t]) t ++; 
27     if(heap[t] < heap[u])
28     {
29       swap(heap[t],heap[u]);
30       u = t; 
31       t = u * 2;
32     }
33     else break;
34   }
35 }
36 void extract() //二叉堆删除堆顶
37 {
38   heap[1]=heap[siz --]; //将堆底移至堆顶,向下调整
39   down(1);
40 }
41 
42 int gettop() //返回堆顶的值
43 {
44   return heap[1];
45 }
46 int main()
47 {
48   cin>>n;
49   for(int i=1; i<=n; i++)
50   {
51      
52     cin >> heap[i]; 
53   }
54   siz = n;
55   for(int i = n / 2;i;i --) down(i);  //建立二叉堆
56   
57   long long ans = 0; //其实这里不会越界,但好像原题数据是3万
58   while(siz >= 2) //如果还可合并
59   {
60     int top1 = gettop(); //取出堆顶(堆中最小值)后删除堆顶
61     extract();
62     int top2 = gettop(); //同上
63     extract();
64     ans+=(top1+top2);
65     insert(top1+top2); //将两数之和加入二叉堆,重复运算
66   }
67   cout<<ans<<endl;
68   return 0;
69 } 
Code
复制代码

 4.P1878 舞蹈课 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

用结构体堆来维护 差值的最小值

每次弹出差值的最小值,并且作上标记,在下一次循环的时候删掉,更新堆顶

注意此题两个相邻的异性可以用异或的性质去简化代码,当我们重连 删除之后的链表(堆)的时候

,我们需要两个数组来存之前 两个堆顶端点的左右值,然后删除后让这两个值左右相连。

复制代码
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 
 4 const int N = 2e5 + 10;
 5 
 6 struct node
 7 {
 8     int l,r;
 9     int v;
10     bool operator<(const node &x) const {
11     if(x.v == v) return x.l < l;
12     return x.v < v;
13     }
14 };
15 priority_queue<node> q;
16 
17 int n;
18 char ch; //字符
19 bool bg[N]; //用来判断字符串数组是否为异性,是为异性就可以入堆
20 int a[N]; 
21 bool is[N]; //标记是否出现过
22 int l[N],r[N]; //表示两个端点的左右元素
23 vector<node> ans;
24 
25 int main()
26 {
27     cin >> n;
28     for (int i = 1; i <= n; i ++ ) cin >> ch,bg[i] = (ch == 'B' ? 1 : 0);
29     for (int i = 1; i <= n; i ++ ) cin >> a[i];
30     for (int i = 1; i <= n; i ++ ) l[i] = i - 1,r[i] = i + 1;
31     
32     is[0] = is[n + 1] = 1;
33     for (int i = 2; i <= n; i ++ ) //如果相邻为异性,丢到堆中,从小到大进行排序
34     if(bg[i] ^ bg[i - 1]) q.push({i - 1,i,abs(a[i - 1] - a[i])});
35     
36     while(!q.empty())
37     { //如果堆顶的元素两个端点之前出现过(已经被标记了),那么没用直接删掉
38         while(!q.empty() && (is[q.top().l] || is[q.top().r])) q.pop(); 
39         if(q.empty()) break; //堆为空直接g;
40         node now = q.top(); //now 为堆顶的三个元素(a[i]元素下标)
41         q.pop(); //删掉并更新堆顶
42         int x = l[now.l],y = r[now.r]; // x为堆顶左端点的左边一个点,y同理
43         l[y] = x,r[x] = y; //让x,y互相相邻
44         if(bg[x] ^ bg[y]) q.push({x,y,abs(a[x] - a[y])}); //x,y互为异性丢到堆里面去
45         ans.push_back(now); //把堆顶的元素丢到 ans里面去
46         is[now.l] = 1,is[now.r] = 1; //标记堆顶两个端点出现过了
47         
48     }
49     cout << ans.size() << endl; //有多少对刚才插入的堆顶元素
50     for (auto i : ans ) cout << i.l << ' ' << i.r << endl; //输出每一组的左右端点
51     return 0;
52 }
Code
复制代码

 

posted @   rw156  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示