建立一个图

核心问题

  1. 怎么表示结点
  2. 怎么表示边以及边权

邻接矩阵

用二维数组表示一张图:数组的下标表示结点,数组的值表示边权。

如 G[1][2] = 11 表示结点1和2之间有条边且边权为2。

如要表示结点1和3之间没有边,可以写作: G[1][3] = 0/-1/INF。(INF是一个很大的数,一般取100,000,000)

const MAXV = 100; //最大顶点数
int n, G[MAXV][MAXV] = 0; //n为顶点数,MAXV为最大顶点数 int main(){
int n,m; //结点数和边数
int u,v,weight; //边的两端点和边权
scanf("%d%d", &n,&m);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &u,&v,&weight);
G[u][v] = weight;
G[v][u] = weight; //如果是无向图
}
}

邻接表

用一维数组的下标表示结点,且数组的每个元素是一个指针,指向一条链表。故每个结点对应一条链表。

每个结点对应链表中存放着它的邻接点和边权,这样就表示出了每个结点所连接的边。

/*直接应用stl库中的vector实现*/
/*可以直接把vector理解为变长数组,这样免去了链表操作的麻烦*/
#include<stdio.h>
#include<vector>
using namespace std; //调用vector
const int MAXV = 100;//最大结点数
struct Node{
int v; //边的终点编号
int w; //边权
Node(int _v, int _w) : v(_v), w(_w) {} //构造函数
};
vector<Node> Adj[MAXV]; //用邻接表表示的图
int n; int main(){
int n,m; //结点数和边数
int u,v,w; //边的两端点和边权
scanf("%d%d", &n,&m);
for(int i = 0; i < m; i++){
scanf("%d%d%d", &u,&v,&w);
Adj[u].push_back(Node(v,w)); //利用构造函数,省去了构建临时结点的麻烦
Adj[v].push_back(Node(u,w)); //如果是无向图
}
}

深度优先遍历(DFS)

DFS就是一路走到黑

具体步骤:

第一部分:给定结点u,遍历u所在的连通块的所有结点

  1. 访问当前结点u
  2. 遍历u的所有邻接点Vi
  3. 如果邻接点Vi还未被访问过,对Vi重复1.2步骤

第二部分:对图G所有结点进行第一部分的操作,即遍历了图的所有连通分量

伪代码

注: vis:标记数组,如果顶点i已被访问,则vis[i] == true。 初始化为 false

DFS(u){ //访问顶点u
vis[u] = true; //设置u已被访问
for(从u出发能到达的所有顶点v){ //枚举从u出发可以到达的所有顶点v
if vis[v] == false //如果v未被访问
DFS(v); //递归访问v
}
}
DFSTrave(G){ //遍历图G
for(G的所有顶点u) //对G的所有顶点u
if vis[u] == flase; //如果u未被访问
DFS(u); //访问u所在的连通块
}

邻接矩阵实现

#include<stdio.h>
const int MAXV = 100; //最大顶点数
const int INF = 100000000; //设INF为一个很大的数 int n, G[MAXV][MAXV]; //n为顶点数,MAXV为最大顶点数
bool vis[MAXV] = {false}; //标记数组,如果顶点i已被访问,则vis[i] == true。 void DFS(int u, int depth){ //u为当前访问的节点标号,depth为深度
vis[u] = true; // 设置u已被访问
//如果需要对u进行一些操作,可以在这里进行
//线面对所有从u出发能到大的分支点顶点进行枚举
for(int v = 0; v < n; v++){ //对每个顶点v
if(vis[v] == false && G[u][v] != INF){ //v未被访问 且 u可以到达v
DFS(v, depth+1); //访问v,深度+1
}
}
} void DFSTrave(){ //遍历图G
for(int u = 0; u < n; u++){ //对每个顶点u
if(vis[u] == false){ //如果u未被访问
DFS(u, 1); //访问u和u所在的连通块,1表示初始为第一层
}
}
}

邻接表实现

#include<stdio.h>
#include<vector>
using namespace std;
const int MAXV = 100; //最大顶点数
const int INF = 100000000; //设INF为一个很大的数 vector<int> Adj[MAXV]; //图G的邻接表
int n; //顶点数
bool vis[MAXV] = {false}; //标记数组 void DFS(int u, int depth){
vis[u] = true;
//如果需要对u进行一些操作,可以在这里进行
for(int i = 0; i < Adj[u].size(); i++){
int v = Adj[u][i];
if(vis[v] == false){
DFS(v, depth+1);
}
}
} void DFSTrave(){ //遍历图G
for(int u = 0; u < n; u++){ //对每个顶点u
if(vis[u] == false){ //如果u未被访问
DFS(u, 1); //访问u和u所在的连通块,1表示初始为第一层
}
}
}

广度优先遍历(BFS)

BFS就是放射状探索

具体步骤

第一部分:给定结点u,遍历u所在的连通块的所有结点

先将起点u加入队列,设置u为已访问

  1. 取出队首元素u
  2. 将u的所有未被访问的邻接点Vi入队,并设置为已访问
  3. 如果队列非空,重复1.2步骤

第二部分:对图G所有结点进行第一部分的操作,即遍历了图的所有连通分量

伪代码

BFS(u){
queue q;
inq[u] = true;
while(q非空){
去除q的队首元素u进行访问;
for(从u出发可达的所有顶点v)
if(inq[v] == false){
将v入队;
inq[v] = true;
}
}
} BFSTrave(G){ //遍历图G
for(G的所有顶点u) //枚举G所有的顶点u
if(inq[u] == false){ //如果u未曾加入过队列
BFS(u); //遍历u所在的连通块
}
}

邻接矩阵实现

#include<stdio.h>
#include<queue> //直接应用stl中定义的队列queue
using namespace std;
#define MAXV 100
#define INF 100000000 int n, G[MAXV][MAXV]; //图的邻接矩阵表示
bool inq[MAXV] = {false}; //标记数组 void BFS(int u){ //遍历u所在的连通块
queue<int> q; //定义队列u
q.push(u); //将初始点u入队
inq[u] = true; //设置u已加入过队列
while(!q.empty()){ //只要队列非空
int u = q.front(); //取出队首元素
q.pop(); //将队首元素出队 for(int v = 0; v < n; v++){
//如果u的邻接点未加入过队列
if(inq[v] == false && G[u][v] != INF){
q.push(v); //入队
inq[v] = true; //标记
//可以在此处根据题目添加一些操作
}
}
}
} void BFSTrave(){ //遍历图G
for(int u = 0; u < n; u++){ //枚举所有顶点
if(inq[u] == false){ //如果u未曾加入过队列
BFS(u); //遍历u所在的连通块
}
}
}

邻接表实现

#include<stdio.h>
#include<queue>
#include<vector>
using namespace std;
#define MAXV 100 vector<int> Adj[MAXV]; //图的邻接表表示
int n;
bool inq[MAXV] = {false}; //标记数组 void BFS(int u){
queue<int> q;
q.push(u);
inq[u] = true;
while(!q.empty()){
int u = q.front();
q.pop();
//只有下面的遍历部分与邻接矩阵写法不同
for(int i=0; i < Adj[u].size(); i++){
int v = Adj[u][i];
if(inq[v] == false){
q.push(v);
inq[u] = true;
//可以在此处根据题目添加一些操作
}
}
}
} void BFSTrave(){
for(int u = 0; u < n; u++){
if(inq[u] == false){
BFS(u);
}
}
}

DFS,BFS遍历方法总结

  1. 前置准备:图G的实现(邻接数组,邻接表),标记数组vis(初始化为false)
  2. 函数主体:递归实现。分两部分。

    函数a:遍历结点s所在的连通块。

    函数b:对遍历图G结点应用函数a。
  3. 注意:遍历前用vis判断是否已被标记

<数据结构>图的构建与基本遍历方法的更多相关文章

  1. python数据结构与算法——二叉树结构与遍历方法

    先序遍历,中序遍历,后序遍历 ,区别在于三条核心语句的位置 层序遍历  采用队列的遍历操作第一次访问根,在访问根的左孩子,接着访问根的有孩子,然后下一层 自左向右一一访问同层的结点 # 先序遍历 # ...

  2. IplImage的数据结构以及遍历方法

    一般我们需要对图像直接进行操作的时候,需要知道图像存储的数据结构,这要也就知道了它的遍历方式 在opencv2.4.4版本下,IplImage的数据结构如下(貌似在别的版本下差别也不会太大) 其中比较 ...

  3. C#与数据结构--图的遍历

    http://www.cnblogs.com/abatei/archive/2008/06/06/1215114.html 8.2 图的存储结构 图的存储结构除了要存储图中各个顶点的本身的信息外,同时 ...

  4. java 完全二叉树的构建与四种遍历方法

    本来就是基础知识,不能丢的太干净,今天竟然花了那么长的时间才写出来,记一下. 有如下的一颗完全二叉树: 先序遍历结果应该为:1  2  4  5  3  6  7 中序遍历结果应该为:4  2  5 ...

  5. Java ——集合框架 list lambda set map 遍历方法 数据结构

    本节重点思维导图 集合框架 有序无序:元素放入的顺序与取出的顺序是否一致,一致即为有序,不一致即无序. List:允许重复.有序 ArrayList:长度可变的数组,遍历速度快 LinkedList: ...

  6. 数据结构——图的深度优先遍历(邻接矩阵表示+java版本)

    ​1.深度优先遍历(DFS) 图的深度优先遍历本质上是一棵树的前序遍历(即先遍历自身,然后遍历其左子树,再遍历右子树),总之图的深度优先遍历是一个递归的过程. 如下图所示,左图是一个图,右图是图的深度 ...

  7. 知识图谱-生物信息学-医学顶刊论文(Briefings in Bioinformatics-2021):生物信息学中的图表示学习:趋势、方法和应用

    4.(2021.6.24)Briefings-生物信息学中的图表示学习:趋势.方法和应用 论文标题: Graph representation learning in bioinformatics: ...

  8. 数据结构--图 的JAVA实现(上)

    1,摘要: 本系列文章主要学习如何使用JAVA语言以邻接表的方式实现了数据结构---图(Graph),这是第一篇文章,学习如何用JAVA来表示图的顶点.从数据的表示方法来说,有二种表示图的方式:一种是 ...

  9. 数据结构--图 的JAVA实现(下)

    在上一篇文章中记录了如何实现图的邻接表.本文借助上一篇文章实现的邻接表来表示一个有向无环图. 1,概述 图的实现与邻接表的实现最大的不同就是,图的实现需要定义一个数据结构来存储所有的顶点以及能够对图进 ...

随机推荐

  1. 【MPI环境配置】 vs2019配置MPI环境

    MPI 即 Message-Passing Interface,提供了一系列并行编程的接口,为了在本机能够学习和使用并行编程,需要提前安装MPI; 配置环境: Microsoft Visual Stu ...

  2. Oracle——生成Awr报告

    Oracle--生成Awr报告 AWR的概念 Oracle数据库是一个使用量很多的数据库,关于Oracle数据库的性能.Oracle10g以后,Oracle提供了一个性能检测的工具:AWR(Autom ...

  3. 规范——Java后端开发规范

    Java后端开发规范 一.技术栈规约 二.命名规范 三.Java代码规范(注释规范.异常与日志.代码逻辑规范) 四.Mybatis与SQL规范 五.结果检查(单元测试及代码扫描) 六.安全规范 一.技 ...

  4. entfrm开源免费模块化无代码开发平台,开放生态为您创造更多的价值

    entfrm开发平台6大特性,赋能快速开发,为您创造更多的价值: 1. 模块化 丰富的模块稳定的框架 后台极易上手 目前已包括系统管理.任务调度.运维监控.开发工具.消息系统.工作流引擎.内容管理等模 ...

  5. 使用$.ajax方式实现页面异步访问,局部更新的效果

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  6. 使用jdbc,查询数据库数据,并将其封装为对象输出

    package cn.itcast.jdbc;import cn.itcast.domain.User;import java.sql.*;import java.util.ArrayList;imp ...

  7. SQL->Python->PySpark计算KS,AUC及PSI

    KS,AUC 和 PSI 是风控算法中最常计算的几个指标,本文记录了多种工具计算这些指标的方法. 生成本文的测试数据: import pandas as pd import numpy as np i ...

  8. centos部署代码仓库gitlab

    目录 一.简介 二.程序部署 部署gitlab 汉化gitlab 三.设置管理员密码 网页方式 指令方式 一.简介 GitLab是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托 ...

  9. Jenkins获取jar包的快照号

    目录 一.简介 二.脚本 一.简介 主要用于打jar包的工程,显示快照包的名字.当jar打包完成后,会在target目录中,截取快照名. 二.脚本 1.脚本return-version.sh #!/b ...

  10. LuoguP7375 [COCI2018-2019#5] Jarvis 题解

    Content 有 \(n\) 架无人机,每架无人机都有一个当前属性值 \(a_i\) 和出战属性值 \(b_i\).你可以给每架无人机的当前属性值同时加一个数 \(x\)(但只能做一次),使得能够出 ...