堆【模板】

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

请你实现一个堆(大根堆)。
操作:
push x:将xxx加入堆。保证xxx为int型整数。不输出任何内容。
top:输出堆顶元素。若堆为空,输出"empty"(不含引号)。
pop:输出堆顶元素,且弹出堆顶。若堆为空,输出"empty"(不含引号)。

输入描述:

第一行输入一个整数nnn (1≤n≤1051\le n \le 10^51≤n≤105),表示操作次数。
接下来的nnn行,每行一个字符串,表示一个操作。保证操作是题目描述中的一种。

输出描述:

按对应操作要求输出。

输入:

11
push 1
top
push 3
top
push 2
top
pop
pop
pop
top
pop

输出:

1
3
3
3
2
1
empty
empty

答案:(优先队列的方法)

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
priority_queue<int>p;
void solve() {
    int n, m;
    cin >> n;
    while (n--) {
        string a;
        cin >> a;
        if (a == "push") {
            cin >> m;
            p.push(m);
        }
        else if (a == "top") {
            if (!p.empty()) {
                cout << p.top() << endl;
            }
            else {
                cout << "empty" << endl;
            }
        }
        else {
            if (!p.empty()) {
                cout << p.top() << endl;
                p.pop();
            }
            else {
                cout << "empty" << endl;
            }
        }
    }
}
signed main() {
    cin.tie(0); cout.tie(0); ios::sync_with_stdio(0);
    int t = 1;
    while (t--) {
        solve();
    }
    return 0;
}

解释:(ai)

您提供的代码实现了一个基于优先队列(在 C++ STL 中是 priority_queue,默认为最大堆)的模拟,用于处理三种操作:push(向堆中添加元素)、top(获取堆顶元素,即最大元素),以及一个未明确标注但实际上是 pop(移除并返回堆顶元素)的操作。不过,根据您的描述和代码实现,我注意到一些可能需要注意的点:

  1. 堆的默认行为:C++ STL 中的 priority_queue 默认是一个最大堆,这意味着它的顶部元素(通过 top() 访问)始终是队列中的最大元素。如果您需要最小堆,应该在声明时指定比较函数或使用 std::priority_queue<int, std::vector<int>, std::greater<int>>

  2. pop 操作:在您的代码中,对于未明确标注的操作(实际上是 pop),您先输出了堆顶元素,然后执行了 pop()。这是正确的实现方式,但请注意,这与您之前提供的基于数组的最小堆实现中的 pop 操作有所不同,那里的 pop 是将堆顶元素与堆的最后一个元素交换,然后调整堆。

  3. 性能考虑:虽然 priority_queue 在大多数情况下表现良好,但如果您需要频繁地进行插入和删除操作,并且关心性能,那么可能需要考虑其他数据结构,如斐波那契堆或配对堆(这些在 C++ STL 中并不可用,但可以在其他库中找到或自己实现)。

  4. 代码风格和可读性:您的代码使用了 #include<bits/stdc++.h> 和 using namespace std;,这在竞赛编程中很常见,但在生产环境中通常不推荐,因为它们可能导致命名冲突和编译时间增加。此外,使用 signed main() 而不是更常见的 int main() 也是不寻常的,虽然这不会改变程序的行为,但可能会让一些读者感到困惑。

  5. 输入/输出:您已经使用了 cin.tie(0); cout.tie(0); ios::sync_with_stdio(0); 来加速输入/输出,这是一个好习惯,尤其是在处理大量输入时。

  6. 测试案例:您的代码中没有包含测试案例的数量(即 t),但您已经将其设置为 1,这意味着程序将只处理一组输入。如果您想要处理多组输入,您应该在 while (t--) 循环中读取 t 的值。

  7. 错误处理:虽然在这个特定的问题中可能不需要复杂的错误处理,但在实际应用中,考虑输入的有效性和异常情况总是一个好习惯。

综上所述,您的代码在功能上是正确的,但可能需要根据实际应用的需求进行一些调整。如果您确实需要最小堆而不是最大堆,那么请相应地更改 priority_queue 的声明。

 

标准大根堆写法:


#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
typedef pair<ll,ll> PII;
vector<int> heap(101010);
int n,sz,tem;
void down(int u){
    int t=u,l=2*u,r=2*u+1;
    if(l<=sz&&heap[t]<heap[l]){
        t=l;
    }
    if(r<=sz&&heap[t]<heap[r]){
        t=r;
    }
    if(t!=u){
        swap(heap[t],heap[u]);
        down(t);
    }
}
void up(int u){
    while(u/2&&heap[u/2]<heap[u]){
        swap(heap[u/2],heap[u]);
        u/=2;
    }
}
int main(){
    cin>>n;
    while(n--){
        string s;
        cin>>s;
        if(s=="push"){
            sz++;
            cin>>tem;
            heap[sz]=tem;
            up(sz);
        }else if(s=="top"){
            if(sz){
                cout<<heap[1]<<endl;
            }else{
                cout<<"empty"<<endl;
            }
        }else{
            if(sz){
                cout<<heap[1]<<endl;
                heap[1]=heap[sz];
                sz--;
                down(1);
            }else{
                cout<<"empty"<<endl;
            }
        }
    }
}

解释:(ai)

这段代码实现了一个基于数组的最小堆(Min Heap)的模拟,并通过命令行接口(CLI)与用户交互,支持三种操作:push(向堆中添加元素)、top(获取堆顶元素,即最小元素)、以及一个未明确标注但实际上是pop(移除并返回堆顶元素)的操作。不过,需要注意的是,代码中对于pop操作的实现方式并不是典型的pop,而是将堆顶元素替换为堆的最后一个元素,然后调整堆以保持其性质。这种实现方式在逻辑上是可行的,但命名上可能会引起混淆。

下面是代码的主要部分及其功能解释:

  1. 变量定义
    • heap:一个全局的整型向量,用于存储堆中的元素。
    • n:表示将要进行的操作数量。
    • sz:表示堆中当前元素的数量。
    • tem:用于临时存储输入的元素值。
  2. down 函数
    • 该函数用于在插入新元素后,从给定的位置u开始,向下调整堆,以保持堆的性质(即父节点的值小于或等于其子节点的值)。
  3. up 函数
    • 该函数用于在插入新元素到堆的末尾后,从该位置开始,向上调整堆,直到满足堆的性质。
  4. 主函数 main
    • 首先读取操作数量n
    • 然后进行n次循环,每次循环读取一个操作字符串s
      • 如果s"push",则读取一个整数tem,将其添加到堆的末尾(sz+1的位置),然后通过up函数调整堆。
      • 如果s"top",则如果堆不为空(sz > 0),输出堆顶元素(即heap[1]),否则输出"empty"
      • 对于其他情况(实际上是模拟的pop操作),如果堆不为空,则输出堆顶元素,将堆的最后一个元素移动到堆顶位置,减小堆的大小sz,然后通过down函数调整堆。

注意

  • 在实际使用中,堆的大小(即sz)通常从0开始计数,但在这段代码中,由于堆存储在heap向量的第一个位置(索引为0)并未使用(或者可以视为哨兵节点),因此堆的大小从1开始计数。这意味着堆的有效元素存储在heap[1]heap[sz]的范围内。
  • 代码中未对heap向量进行初始化或设置初始大小,但由于使用了vector的动态特性,这并不影响其功能。然而,在实际应用中,可能需要考虑向量的初始大小以优化性能。
  • 代码中对于pop操作的命名不够直观,可能会导致理解上的混淆。在实际应用中,建议明确命名以反映其实际功能。

 

 

 

 

  • 23
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用优化的Dijkstra算法模板: ```cpp #include <iostream> #include <vector> #include <queue> #include <limits> using namespace std; typedef pair<int, int> pii; // 存储节点和距离的pair const int INF = numeric_limits<int>::max(); // 无穷大 vector<vector<pii>> graph; // 图的邻接表表示 vector<int> dijkstra(int start, int n) { vector<int> dist(n, INF); // 存储起始点到每个点的最短距离 dist[start] = 0; priority_queue<pii, vector<pii>, greater<pii>> pq; // 小顶,存储节点和距离的pair pq.push(make_pair(0, start)); // 将起始点入队 while (!pq.empty()) { int u = pq.top().second; // 当前节点 int d = pq.top().first; // 当前距离 pq.pop(); // 如果当前距离大于已经计算出的最短距离,则忽略 if (d > dist[u]) continue; // 遍历当前节点的邻接节点 for (auto edge : graph[u]) { int v = edge.first; // 邻接节点 int w = edge.second; // 边的权重 // 如果通过u到v的距离更短,则更新最短距离,并将v加入队列 if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pq.push(make_pair(dist[v], v)); } } } return dist; } int main() { int n, m; cin >> n >> m; // 输入节点数和边数 graph.resize(n); for (int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; // 输入边的起点、终点和权重 graph[u].push_back(make_pair(v, w)); graph[v].push_back(make_pair(u, w)); // 无向图需要双向连接 } int start; cin >> start; // 输入起始点 vector<int> dist = dijkstra(start, n); // 调用Dijkstra算法 // 输出最短距离 for (int i = 0; i < n; i++) cout << "Node " << i << ": " << dist[i] << endl; return 0; } ``` 这个模板使用了优化的方法来实现Dijkstra算法,通过优先队列(小顶)来选择当前距离最短的节点进行扩展。算法的时间复杂度为O((V+E)logV),其中V为节点数,E为边数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值