邻接表(vector<node> e[100]; e[x].push_back( (node){y,z} ); node(int a,int b):y(a),z(b){};)

本文探讨了图数据结构的多种存储方式,包括邻接表、边集表及使用std::map和std::set存储无向图和有向图的方法。详细介绍了如何优化存储效率,解决反向边访问问题,以及在不同场景下选择合适的数据结构进行图存储和遍历。

 

struct node
{
    int y,z;
    node(int a,int b):y(a),z(b){};
};

vector<node> e[100];//邻接表
 for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
       e[x].push_back( (node){y,z} );
       //e[y].push_back((node){x,z});
    //存入无向图
    }

对第一种方法有种优化就是对每个点所对应的边的向量进行预估。例如有m条有向边,n个点,那么每个向量需要reserve(6*(m/n)/5);一般这样存储效率会有显著提高。

#include <vector>
#include <iostream>
using namespace std;
int main()
{
    vector<vector<size_t>> graph(5);
    graph[0].push_back(1);//V0->V1.
    graph[1].push_back(4);//V1->V4.
    graph[1].push_back(0);//V1->V0.
    graph[2].push_back(1);//V2->V1.
    graph[2].push_back(3);//V2->V3.
    graph[3].push_back(0);//V3->V0.
    graph[4].push_back(3);//V4->V3.
    //假定要访问点1.
    for(const auto &ele:graph[1])//对于全部属于graph[1]这个容器的元素
    std::cout<<ele<<'';
    std::cout<<std::endl;
    //程序运行后输出40,表示点1连接了4点和0点。
    return 0;
}

第二种(使用map,set):

方法太多,不再举例了。

然而我们这样存图是不够的,对于无向图而言,可能存在一条边需要知道自己的反向边的位置。例如欧拉回路,例如网络流都是需要的。

第二种方法由于std::map<std::size_t,std::set<std::size_t>> graph;只是离散化后的邻接矩阵。对于这种存图方法,访问反向边则非常简单,例如我访问的是a->b这样一条边。那么只需要graph[b].count(a);就可以访问其反向边b->a。

然而对于第一种方法,我们没有办法解决反向边的互相访问题。

 

所以我们对于这种图需要存图修正。代码如下:

对于list容器可以直接存迭代器的,但是存图时也得考虑a是否等于b.forward_list存反向边的图就不好,因为用链表存图就是需要存完图后插入删除,假定一个元素前面的元素被删除了,那么根本无法访问反向边!!!!

感觉存图没问题了?NO!!!!还有一种图更奇葩,那就是对于每个点中的边得排序又得知道反向边的那种图。USACO上有个题目叫做骑马修栅栏,那个题要求字典序输出。数据量很小,以至于可以直接矩阵存图,但是我们考虑如何数据量大,这种方法就不行了。如果用第二种方法(std::map<std::size_t,std::set<std::size_t>>)存图,绝对可以,但是相对常数较大。如果我们考虑顺序容器去存储则比较快一些。

方法就是先用边集表存图,然后每条边a,b得优先以std::min(a,b)为第一关键字再按std::max(a,b)为第二关键字排序,再按照修正后的存图方法存图即可。具体代码见nocow上骑马修栅栏那题lgeecn发的题解和代码。

如果使用list存图可以先存图再用list.sort()函数进行排序,不过似乎效率会差一些,毕竟相对于vector,list常数太大了,达到6倍以上。

存图真心不简单,因为真正用的时候你可能会遇到各种问题,但是你可以加以思考,进行容器搭配使用,即可解决。

#include<map>
#include<set>
#include<iostream>
#include<cstddef>
#include<map>
#include<set>
intmain()
{
std::map<std::size_t,std::set<std::size_t>>graph;
graph[0].insert(1);//V0->V1.
graph[1].insert(4);//V1->V4.
graph[1].insert(0);//V1->V0.
graph[2].insert(1);//V2->V1.
graph[2].insert(3);//V2->V3.
graph[3].insert(0);//V3->V0.
graph[4].insert(3);//V4->V3.
//假定要访问点1.
for(constauto&ele:graph[1])//对于全部属于graph[1]这个容器的元素
std::cout<<ele<<'';
std::cout<<std::endl;
//程序运行后输出04,表示点1连接了0点和4点。对map,set里的元素进行遍历是有序的
return0;
}

 

#include<vector>
#include<iostream>
#include<utility>
#include<cstddef>
intmain()
{
std::vector<std::vector<std::pair<std::size_t,std::size_t>>>graph(5);//每条边的第二个元素不能是迭代器!!会失效!!!!必须是下标!!!
//比如有一条a-b的边,我们得让它实现互访。我们这里假定a=2,b=4;
std::size_ta(2),b(4);
graph[a].push_back(std::make_pair(b,graph[b].size()+(a==b)));//Va->Vb.需要判定a是否等于b.
graph[b].push_back(std::make_pair(a,graph[a].size()-1));//Vb->Va,这个不需要判定a是否等于b.
//访问反向边的方法.
for(constauto&ele:graph[a])//访问点a
graph[ele.first][ele.second];//这就是边ele的反向边了
return0;
}

 

#include <iostream> #include <vector> #include <queue> #include <stack> #include <map> using namespace std; // 邻接矩阵实现 class AdjMatrix { private: vector<char> vertices; vector<vector<int> > matrix; map<char, int> indexMap; public: AdjMatrix(vector<char> nodes) : vertices(nodes) { // 初始化顶点索引映射 for (int i = 0; i < nodes.size(); i++) { indexMap[nodes[i]] = i; } // 初始化邻接矩阵为全0 matrix.resize(nodes.size(), vector<int>(nodes.size(), 0)); } // 添加无向边 void addEdge(char u, char v) { int i = indexMap[u]; int j = indexMap[v]; matrix[i][j] = 1; matrix[j][i] = 1; } // 打印邻接矩阵 void printMatrix() { cout << "邻接矩阵:" << endl; cout << " "; for (int i = 0; i < vertices.size(); i++) { cout << vertices[i] << " "; } cout << endl; for (int i = 0; i < vertices.size(); i++) { cout << vertices[i] << " "; for (int j = 0; j < matrix[i].size(); j++) { cout << matrix[i][j] << " "; } cout << endl; } } }; // 邻接表实现 class AdjList { private: map<char, vector<char> > adjList; vector<char> vertices; public: AdjList(vector<char> nodes) : vertices(nodes) { for (int i = 0; i < nodes.size(); i++) { adjList[nodes[i]] = vector<char>(); } } // 添加无向边 void addEdge(char u, char v) { adjList[u].push_back(v); adjList[v].push_back(u); } // 深度优先遍历(非递归) vector<char> DFS(char start) { vector<char> result; stack<char> s; map<char, bool> visited; s.push(start); while (!s.empty()) { char v = s.top(); s.pop(); if (!visited[v]) { visited[v] = true; result.push_back(v); // 逆序邻接节点保证与递归顺序一致 vector<char> neighbors = adjList[v]; for (vector<char>::reverse_iterator it = neighbors.rbegin(); it != neighbors.rend(); ++it) { if (!visited[*it]) { s.push(*it); } } } } return result; } // 广度优先遍历 vector<char> BFS(char start) { vector<char> result; queue<char> q; map<char, bool> visited; q.push(start); visited[start] = true; while (!q.empty()) { char v = q.front(); q.pop(); result.push_back(v); vector<char> neighbors = adjList[v]; for (vector<char>::iterator it = neighbors.begin(); it != neighbors.end(); ++it) { if (!visited[*it]) { visited[*it] = true; q.push(*it); } } } return result; } // 打印邻接表 void printList() { cout << "\n邻接表:" << endl; for (map<char, vector<char> >::iterator it = adjList.begin(); it != adjList.end(); ++it) { cout << it->first << ": "; vector<char> neighbors = it->second; for (vector<char>::iterator vit = neighbors.begin(); vit != neighbors.end(); ++vit) { cout << *vit << " "; } cout << endl; } } }; int main() { // 初始化顶点集合(C++98 风格) vector<char> nodes; nodes.push_back('a&#39;); nodes.push_back('c&#39;); nodes.push_back('d&#39;); // 邻接矩阵测试 AdjMatrix graphMatrix(nodes); graphMatrix.addEdge('a', 'c&#39;); graphMatrix.addEdge('c', 'd&#39;); graphMatrix.addEdge('d', 'a&#39;); graphMatrix.printMatrix(); // 邻接表测试 AdjList graphList(nodes); graphList.addEdge('a', 'c&#39;); graphList.addEdge('c', 'd&#39;); graphList.addEdge('d', 'a&#39;); graphList.printList(); // 遍历测试 vector<char> dfsResult = graphList.DFS('a&#39;); vector<char> bfsResult = graphList.BFS('a&#39;); cout << "\nDFS遍历结果: "; for (vector<char>::iterator it = dfsResult.begin(); it != dfsResult.end(); ++it) { cout << *it << " "; } cout << "\nBFS遍历结果: "; for (vector<char>::iterator it = bfsResult.begin(); it != bfsResult.end(); ++it) { cout << *it << " "; } return 0; } 为什么这个代码在DevC++上运行不出来,正确的完整代码是什么
05-27
#include <iostream> #include <vector> #include <queue> #include <unordered_map> #include <unordered_set> #include <algorithm> #include <string> using namespace std; struct Path { vector<string> nodes; int cost; Path() : cost(0) {} Path(vector<string> n, int c) : nodes(n), cost(c) {} // 小根堆:按cost升序排列(修复运算符重载逻辑) bool operator>(const Path& other) const { return cost > other.cost; } }; class KShortestPaths { private: unordered_map<string, vector<pair<string, int>>> graph; unordered_set<string> banned; int maxTrans; // 最大中继点数,负值表示无限制 public: KShortestPaths() : maxTrans(-1) {} void addEdge(const string& u, const string& v, int w) { graph[u].emplace_back(v, w); graph[v].emplace_back(u, w); } void banNode(const string& node) { banned.insert(node); } void unbanNode(const string& node) { banned.erase(node); } void setMaxTrans(int y) { maxTrans = y; } vector<Path> findKShortestPaths(const string& start, const string& end, int k) { vector<Path> result; // 检查起点/终点是否存在 if (graph.find(start) == graph.end() || graph.find(end) == graph.end()) { return result; } // 小根堆:用greater<Path>指定比较规则(修复堆类型) priority_queue<Path, vector<Path>, greater<Path>> pq; pq.emplace(vector<string>{start}, 0); while (!pq.empty() && result.size() < static_cast<size_t>(k)) { Path curr = pq.top(); pq.pop(); const string& last = curr.nodes.back(); // 到达终点:检查中继数约束 if (last == end) { int transCount = curr.nodes.size() - 2; // 中继数=节点数-2 if (maxTrans < 0 || transCount <= maxTrans) { result.push_back(curr); } continue; } // 遍历邻居 for (const auto& edge : graph[last]) { const string& next = edge.first; int weight = edge.second; // 避免环(路径中不能重复节点) if (find(curr.nodes.begin(), curr.nodes.end(), next) != curr.nodes.end()) { continue; } // 黑名单约束:非起点/终点的节点不能在黑名单中 if (banned.count(next) && next != start && next != end) { continue; } // 构建新路径 Path newPath = curr; newPath.nodes.push_back(next); newPath.cost += weight; pq.push(newPath); } } return result; } }; int main() { KShortestPaths solver; int m, n; cin >> m >> n; vector<string> vertices(m); for (int i = 0; i < m; ++i) { cin >> vertices[i]; } for (int i = 0; i < n; ++i) { string u, v; int w; cin >> u >> v >> w; solver.addEdge(u, v, w); } // 输出提示信息(匹配需求格式) cout << "Please enter your next request." << endl; cout << "Options include: ban X, unban X, maxTrans Y, paths A B k, quit." << endl; string command; while (cin >> command) { if (command == "quit&quot;) { break; } else if (command == "ban&quot;) { string node; cin >> node; solver.banNode(node); cout << endl; } else if (command == "unban&quot;) { string node; cin >> node; solver.unbanNode(node); cout << endl; } else if (command == "maxTrans&quot;) { int y; cin >> y; solver.setMaxTrans(y); cout << endl; } else if (command == "paths&quot;) { string start, end; int k; cin >> start >> end >> k; auto paths = solver.findKShortestPaths(start, end, k); // 输出结果(匹配需求格式) cout << "\nThere are " << paths.size() << " paths." << endl; for (size_t i = 0; i < paths.size(); ++i) { const Path& p = paths[i]; cout << "Path " << i << ":"; for (size_t j = 0; j < p.nodes.size(); ++j) { if (j > 0) cout << "->"; cout << p.nodes[j]; } cout << ", distance = " << p.cost << endl; } cout << endl; } else { cout << "\n无效指令!\n" << endl; cin.ignore(1024, '\n&#39;); } } return 0; }
最新发布
12-23
#include <iostream> #include <vector> #include <algorithm> using namespace std; vector<int> findEulerianPath(int n, vector<vector<int>>& graph) { vector<int> inDegree(n, 0), outDegree(n, 0); vector<vector<int>> adj(n); // 计算入度和出度,并构建邻接表 for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { outDegree[i] += graph[i][j]; inDegree[j] += graph[i][j]; for (int k = 0; k < graph[i][j]; k++) { adj[i].push_back(j); } } } // 检查是否存在欧拉路径或回路 int startNode = 0, endNode = 0; bool hasEulerPath = false, hasEulerCircuit = true; for (int i = 0; i < n; i++) { if (outDegree[i] != inDegree[i]) { hasEulerCircuit = false; if (outDegree[i] - inDegree[i] == 1) { startNode = i; hasEulerPath = true; } else if (inDegree[i] - outDegree[i] == 1) { endNode = i; } else { return {}; // 不存在欧拉路径 } } } if (!hasEulerPath && !hasEulerCircuit) { return {}; } // 确保字母顺序最小 for (auto& edges : adj) { sort(edges.begin(), edges.end()); } // Hierholzer算法找欧拉路径 vector<int> path; vector<int> stack; stack.push_back(hasEulerPath ? startNode : 0); while (!stack.empty()) { int u = stack.back(); if (!adj[u].empty()) { int v = adj[u].back(); adj[u].pop_back(); stack.push_back(v); } else { path.push_back(stack.back()); stack.pop_back(); } } reverse(path.begin(), path.end()); // 检查是否遍历了所有边 int totalEdges = 0; for (int i = 0; i < n; i++) { totalEdges += outDegree[i]; } if (path.size() != totalEdges + 1) { return {}; } return path; } int main() { int n; cin >> n; vector<vector<int>> graph(n, vector<int>(n)); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { cin >> graph[i][j]; } } vector<int> path = findEulerianPath(n, graph); if (path.empty()) { cout << "Nothing found." << endl; } else { // 检查是否是回路 bool isCircuit = (path.front() == path.back()); if (isCircuit) { cout << "Euler circuit found:" << endl; } else { cout << "Euler path found:" << endl; } // 输出路径 for (int i = 0; i < path.size(); i++) { cout << char('a' + path[i]); if (i != path.size() - 1) { cout << " "; } } cout << endl; } return 0; }分析这段代码对于解决上面问题有什么错误或遗漏
06-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值