当前位置: 首页 > news >正文

星际旅行家(广度优先搜索+邻接表)

我对这两个其实都不是那么熟悉。

我们略微用一用。

1. 邻接表基本概念

邻接表(Adjacency List)是一种图的存储结构,它通过为每个顶点维护一个链表来存储该顶点的所有邻接顶点。

优点​:

  • 节省空间(特别是稀疏图)
  • 容易找到某个顶点的所有邻接顶点
  • 添加/删除边操作高效

缺点​:

  • 判断两个顶点是否相邻需要遍历链表
  • 相比邻接矩阵,某些操作效率较低

C++中邻接表的实现方式

2.1 使用vector<vector<int>>

这是最常用的实现方式,简洁高效:

vector<vector<int>> adj(n);  // n为顶点数// 添加边(无向图)
adj[u].push_back(v);
adj[v].push_back(u);// 添加边(有向图)
adj[u].push_back(v);

2.2 使用list<int>数组

list<int> *adj = new list<int>[n];// 添加边
adj[u].push_back(v);

2.3 使用结构体/类(带权图)

struct Edge {int to;int weight;
};vector<vector<Edge>> adj(n);// 添加带权边
adj[u].push_back({v, w});

3. 邻接表的基本操作

3.1 初始化

int n = 5; // 顶点数
vector<vector<int>> adj(n);

3.2 添加边

// 无向图
void addEdge(int u, int v) {adj[u].push_back(v);adj[v].push_back(u);
}// 有向图
void addDirectedEdge(int u, int v) {adj[u].push_back(v);
}

3.3 删除边

// 无向图删除边
void removeEdge(int u, int v) {// 删除u->vadj[u].erase(remove(adj[u].begin(), adj[u].end(), v), adj[u].end());// 删除v->uadj[v].erase(remove(adj[v].begin(), adj[v].end(), u), adj[v].end());
}

3.4 遍历邻接顶点

// 遍历顶点u的所有邻接顶点
for(int v : adj[u]) {cout << v << " ";
}

4. 邻接表的常见应用

4.1 图的遍历

DFS实现​:

vector<bool> visited(n, false);void dfs(int u) {visited[u] = true;for(int v : adj[u]) {if(!visited[v]) {dfs(v);}}
}

BFS实现​:

void bfs(int start) {vector<bool> visited(n, false);queue<int> q;q.push(start);visited[start] = true;while(!q.empty()) {int u = q.front();q.pop();for(int v : adj[u]) {if(!visited[v]) {visited[v] = true;q.push(v);}}}
}

4.2 拓扑排序

vector<int> topologicalSort() {vector<int> inDegree(n, 0);vector<int> result;queue<int> q;// 计算入度for(int u = 0; u < n; u++) {for(int v : adj[u]) {inDegree[v]++;}}// 入度为0的节点入队for(int u = 0; u < n; u++) {if(inDegree[u] == 0) {q.push(u);}}// 拓扑排序while(!q.empty()) {int u = q.front();q.pop();result.push_back(u);for(int v : adj[u]) {if(--inDegree[v] == 0) {q.push(v);}}}return result;
}

5. 性能优化技巧

  1. 预分配空间​:如果知道大概的边数,可以预先reserve空间

    for(auto& list : adj) {list.reserve(expected_edges_per_vertex);
    }
  2. 使用emplace_back​:避免临时对象构造

    adj[u].emplace_back(v);
  3. 使用数组代替vector​:对于固定大小的图,性能更好

    vector<int> adj[MAX_NODES];
  4. 使用前向星​:对于极大图,可以节省空间

    struct Edge {int to, next;
    } edges[MAX_EDGES];
    int head[MAX_NODES], edge_count;

6. 邻接表的变体

6.1 带权图的邻接表

struct Edge {int to;int weight;
};vector<vector<Edge>> adj(n);// 添加带权边
adj[u].push_back({v, w});

6.2 使用pair的邻接表

vector<vector<pair<int, int>>> adj(n); // pair<to, weight>// 添加带权边
adj[u].emplace_back(v, w);

6.3 链式前向星

const int MAXN = 100010;
const int MAXM = 200010;struct Edge {int to, next, w;
} edge[MAXM];int head[MAXN], cnt;void init() {memset(head, -1, sizeof(head));cnt = 0;
}void addEdge(int u, int v, int w) {edge[cnt].to = v;edge[cnt].w = w;edge[cnt].next = head[u];head[u] = cnt++;
}

7. 常见问题与解决方案

7.1 处理重复边

// 使用set代替vector
vector<set<int>> adj(n);// 添加边(自动去重)
adj[u].insert(v);
adj[v].insert(u);

7.2 处理重边(允许重复边)

// 直接使用vector即可
vector<vector<int>> adj(n);
adj[u].push_back(v); // 允许重复添加

7.3 快速查询边是否存在

// 使用unordered_set
vector<unordered_set<int>> adj(n);// 查询边u->v是否存在
if(adj[u].count(v)) {// 边存在
}

 

上面其实已经总结邻接表与DFS和BFS结合使用的实例了

那我们直接看题:

代码:
 

# include<iostream>
# include<vector>
# include<queue>using namespace std;int main()
{int n,m;cin>>n>>m;vector<vector<int>> arr(n+1);vector<int> visited(n+1,false);queue<int> q;int u,v;for(int i=0;i<m;i++){cin>>u>>v;arr[u].push_back(v);arr[v].push_back(u);}q.push(1);visited[1] = true;while(!q.empty()){int current = q.front();for(int i:arr[current]){if(!visited[i]){q.push(i);visited[i] = true;}}q.pop();}int unvisited = 0;for(int i=2;i<=n;i++){if(!visited[i]){unvisited++;}}cout<<unvisited<<endl;return 0;
}

ok,其实没有太大必要去详细讲解,你从头仔细看肯定可以看懂的,心静如水,相信自己!

http://www.xdnf.cn/news/641575.html

相关文章:

  • 直流电机 pwm 调速
  • 第五十一节:增强现实基础-单应性矩阵计算
  • MySQL#Select语句执行过程
  • LLMs之Qwen:《Qwen3 Technical Report》翻译与解读
  • 2025年5月系分论文题(回忆版)
  • C# 怎么做chat柱状图能实现不同的颜色,还带游标
  • 篇章二 基础——包装类
  • ADS学习笔记(二) 交流小信号仿真
  • Windows逆向工程提升之x86结构化异常SEH处理机制
  • Java 可扩展状态系统设计:备忘录模式的工程化实践与架构演进
  • TCP建立连接为什么不是两次握手,而是三次,为什么不能在第二次握手时就建立连接?
  • 基于AI自动生成测试用例
  • 有限时间 vs 固定时间 vs 预定时间滑模:稳定性分析与仿真验证方法对比(中)
  • 8.Java 8 日期时间处理:从 Date 的崩溃到 LocalDate 的优雅自救​
  • 【黑马点评】redis实战
  • Seaborn库的定义与核心功能
  • 【linux】mount命令中,data=writeback参数详细介绍
  • ubuntu 22.04安装和使用docker介绍
  • Java面向对象 二
  • GitHub Copilot 现已支持 AI Coding Agent
  • MySQL:12_视图
  • 08_模型训练篇-Torchvision(下):其他有趣的功能
  • 文件操作(C语言版)
  • 12.LCD、FSMC和ILI9341芯片
  • python中pandas之dataframe知识
  • 文本存入向量数据库流程
  • Python海龟绘图(turtle模块)常考知识点总结
  • 【数据结构】线性表之“双链表(带头循环双向链表)”
  • java 加密算法的简单使用
  • Linux系统中实时查看日志