对于优先队列的一些思考

本文详细介绍了C++标准库中priority_queue的使用方法,包括默认的大顶堆行为、如何通过自定义比较函数创建小顶堆,以及如何为自定义类型设置优先级规则。
/*
为什么默认是大顶堆呢,考虑priority_queue的实现容器,vector显然在尾部删除快,默认重载<运算符,也就是说vector中的元素从小到大排列,尾删的时候取出最大值,所以pq.top()取出最大值,pq.pop()删除最大值。
*/
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
#ifdef LOCAL_DEBUG
freopen("input.txt","r",stdin);
#endif
    priority_queue<int> pq;
    int a[]={1,2,3,4,5};
    int len=sizeof(a)/sizeof(int);
    for(int i=0;i<len;i++)
        pq.push(a[i]);
    while(!pq.empty())
    {
        cout<<pq.top()<<endl;
        pq.pop();
    }
    return 0;
}

/*
使用vector容器,定义greater<int>比较函数,这个函数使vector中的元素从大到小排序,那么显然,尾删的时候取出最小值。
*/

#include <map>
#include <set>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

int main()
{
#ifdef LOCAL_DEBUG
freopen("input.txt","r",stdin);
#endif
    priority_queue<int> pq;
    int a[]={1,2,3,4,5};
    int len=sizeof(a)/sizeof(int);
    for(int i=0;i<len;i++)
        pq.push(a[i]);
    while(!pq.empty())
    {
        cout<<pq.top()<<endl;
        pq.pop();
    }
    return 0;
}

/*
对于自定义的结构,那么按照前面的说法,首先会按照你给出的比较运算符排序,然后pq取数的时候取出尾巴上的值,如下对node结构自定义<运算符,pq每次优先取出最大的
*/

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

struct Node {
    int x,y;
    Node(int xx=0,int yy=0):x(xx),y(yy) {}
    bool operator <(const Node &t) const {
        if(x<t.x) return true;
        else if(x==t.x && y<t.y) return true;
        return false;
    }
};

int main()
{
#ifdef LOCAL_DEBUG
freopen("input.txt","r",stdin);
#endif
    srand((unsigned)time(NULL));
    priority_queue<Node> pq;
    for(int i=0;i<10;i++)
    {
        pq.push(Node(rand()%5,rand()%5));
    }
    while(!pq.empty())
    {
        Node t=pq.top(); pq.pop();
        cout<<t.x<<" "<<t.y<<endl;
    }
    return 0;
}

/*优先取出最小的元素*/

#include <map>
#include <set>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

struct Node {
    int x,y;
    Node(int xx=0,int yy=0):x(xx),y(yy) {}
    bool operator <(const Node &t) const {
        if(x>t.x) return true;
        else if(x==t.x && y>t.y) return true;
        return false;
    }
};

int main()
{
#ifdef LOCAL_DEBUG
freopen("input.txt","r",stdin);
#endif
    srand((unsigned)time(NULL));
    priority_queue<Node> pq;
    for(int i=0;i<10;i++)
    {
        pq.push(Node(rand()%5,rand()%5));
    }
    while(!pq.empty())
    {
        Node t=pq.top(); pq.pop();
        cout<<t.x<<" "<<t.y<<endl;
    }
    return 0;
}

<think>好的,我现在需要帮助用户解答如何在不使用优先队列的情况下实现无向图的Dijkstra算法。用户之前已经询问过使用优先队列的版本,现在可能想了解不同的实现方法者优化方向。首先,我需要确认用户的需求。用户可能对优先队列的效率有疑问,者想了解更基础的实现方式,比如用普通队列其他数据结构替代。 接下来,我需要回忆Dijkstra算法的基本原理。Dijkstra的核心是每次选取当前距离最短的节点进行松弛操作。使用优先队列(通常是小顶堆)可以高效地获取下一个最短距离的节点,时间复杂度为O((V+E) log V)。如果不用优先队列,可能需要用其他方式选择节点,比如线性搜索,但这样时间复杂度会上升到O(V² + E),对于稀疏图来说效率较低,但对于小规模数据可能适用。 用户可能的应用场景是什么?如果用户处理的是节点数量较少的图,线性搜索的实现虽然效率低,但代码简单,容易理解。者用户可能在学习算法,想了解不同实现方式的优缺点。另外,用户可能对优先队列的实现机制不熟悉,想通过更基础的方法来加深理解。 接下来,我需要规划回答的结构。首先介绍不用优先队列的Dijkstra算法,然后分步骤说明如何实现,包括初始化、选择节点的方式、松弛操作等。同时,需要比较两种方法的时间复杂度,指出各自的适用场景。还要给出代码示例,帮助用户理解具体实现。 需要注意用户之前的问题中已经提到了邻接表和无向图的处理,所以这部分可以简略提及,重点放在不使用优先队列的实现差异上。需要确保代码正确,比如使用普通队列时如何处理节点的重复入队,是否需要标记已处理的节点等。在原始Dijkstra算法中,一旦节点的最短距离确定后就不会再被处理,但使用队列时可能需要多次检查。 可能的误区是用户认为不用优先队列只能用队列,但实际上可能有其他数据结构,比如不使用任何优化,直接每次线性扫描选择最小距离节点。这时候需要明确说明这种方法的名称,比如“朴素的Dijkstra”“未优化的Dijkstra”,并解释其实现方式。 另外,需要提醒用户这种方法的时间复杂度较高,适用于小规模数据,避免用户在大规模图上误用导致性能问题。同时,可以对比优先队列版本,说明各自的优缺点,帮助用户做出合适的选择。 最后,确保代码示例正确,比如在每次循环中找到当前距离最小的节点,使用标记数组避免重复处理,者允许节点被多次处理但通过距离判断来跳过无效的松弛操作。需要测试代码是否存在逻辑错误,比如是否正确更新距离,是否处理了所有可能的边等。 总结来说,我需要详细解释不使用优先队列的实现方法,对比两种方式的差异,指出适用场景,并提供正确且易懂的代码示例,同时提醒用户注意时间复杂度的问题。</think>以下是**不使用优先队列**实现无向图Dijkstra算法的分步解析(基于朴素的线性扫描方式): --- ### **1. 邻接表表示** 与优先队列版本相同,用`vector<vector<pair<int, int>>>`存储无向图: ```cpp vector<vector<pair<int, int>>> graph(n); // n为节点总数 // 添加无向边需双向操作 graph[u].push_back({v, w}); graph[v].push_back({u, w}); ``` --- ### **2. 核心实现(朴素版)** #### **步骤分解** 1. **初始化距离数组和访问标记**: ```cpp vector<int> dist(n, INF); // 距离初始化为无穷大 vector<bool> visited(n, false); // 标记已确定最短距离的节点 dist[start] = 0; // 起点距离为0 ``` 2. **循环遍历所有节点**: ```cpp for (int i = 0; i < n; ++i) { // 需要遍历n次 // 步骤1:找到当前未访问且距离最小的节点u int u = -1; int min_dist = INF; for (int j = 0; j < n; ++j) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; u = j; } } if (u == -1) break; // 所有可达节点已处理完 // 步骤2:标记u为已访问 visited[u] = true; // 步骤3:松弛u的邻接边 for (auto &edge : graph[u]) { int v = edge.first; int w = edge.second; if (!visited[v] && dist[v] > dist[u] + w) { dist[v] = dist[u] + w; // 更新最短距离 } } } ``` --- ### **3. 完整代码示例** ```cpp #include <bits/stdc++.h> using namespace std; const int INF = 1e9; void dijkstra_no_heap(int start, vector<vector<pair<int, int>>>& graph, vector<int>& dist) { int n = graph.size(); dist.assign(n, INF); vector<bool> visited(n, false); dist[start] = 0; for (int i = 0; i < n; ++i) { // 寻找当前未访问的最小距离节点 int u = -1; int min_dist = INF; for (int j = 0; j < n; ++j) { if (!visited[j] && dist[j] < min_dist) { min_dist = dist[j]; u = j; } } if (u == -1) break; visited[u] = true; // 松弛邻接边 for (auto &edge : graph[u]) { int v = edge.first; int w = edge.second; if (!visited[v] && dist[v] > dist[u] + w) { dist[v] = dist[u] + w; } } } } int main() { int n = 5; vector<vector<pair<int, int>>> graph(n); // 添加无向边示例(同上一个示例) graph[0].push_back({1, 2}); graph[1].push_back({0, 2}); graph[1].push_back({2, 3}); graph[2].push_back({1, 3}); vector<int> dist; dijkstra_no_heap(0, graph, dist); for (int i = 0; i < n; ++i) { cout << "节点" << i << "最短距离:" << dist[i] << endl; } return 0; } ``` --- ### **4. 关键点说明** - **时间复杂度**:$$O(V^2 + E)$$,其中$V$为节点数,$E$为边数 **适用场景**:节点数较少(如$V \leq 1000$)完全图($E \approx V^2$) - **与优先队列版的对比**: | 特性 | 朴素版 | 优先队列版 | |--------------------|---------------------|----------------------| | 时间复杂度 | $$O(V^2 + E)$$ | $$O((V+E)\log V)$$ | | 空间复杂度 | $$O(V)$$ | $$O(V + E)$$ | | 适用场景 | 小规模稠密图 | 大规模稀疏图 | - **无向图处理**:仍需双向添加边(与优先队列版一致) - **松弛操作差异**:直接更新距离,无需处理队列中的重复节点 --- ### **5. 延伸思考** - **优化方向**:若需提升效率但不用优先队列,可结合**双端队列**(Deque)实现近似$$O(V + E)$$的SPFA算法(适用于无负权边的图) - **代码可读性**:朴素版更易理解Dijkstra的核心逻辑(贪心选择过程) 通过这种方式,可以清晰展示Dijkstra算法的基础原理,但需注意其性能局限性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值