建立一个图

核心问题

  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. 数仓day01

    1. 该项目适用哪些行业? 主营业务在线上进行的一些公司,比如外卖公司,各类app(比如:下厨房,头条,安居客,斗鱼,每日优鲜,淘宝网等等) 这类公司通常要针对用户的线上访问行为.消费行为.业务操作行 ...

  2. const与指针的三种形式

    使用指针时涉及到两个对象:该指针本身和被它所指的对象. 将一个指针的声明用const"预先固定"将使那个对象而不是使这个指针成为常量.要将指针本身而不是被指对象声明为常量,必须使用 ...

  3. c学习 - 算法

    简介: 一个程序包括两方面内容:数据结构.算法 数据结构:对数据的描述,包括数据的类型和数据的组织形式 算法:对操作的描述,即操作步骤 (程序=算法+数据结构) 算法是灵魂,数据结构是加工对象,语言是 ...

  4. mysql explain using filesort

    创建表,字段tid上无索引(mysql 5.7) CREATE TABLE `test` ( `tid` int(11) DEFAULT NULL, `tname` varchar(12) DEFAU ...

  5. 查看IP访问量的shell脚本汇总

    第一部分,1,查看TCP连接状态 netstat -nat |awk '{print $6}'|sort|uniq -c|sort -rn netstat -n | awk '/^tcp/ {++S[ ...

  6. linux下把一个用户从某个组中删除,而不删除用户

    查看当前用户/登录用户 基本语法 whoami / who am I 用户组 介绍 类似于角色,系统可以对有共性的多个用户进行统一的管理. 新增组 语法 groupadd 组名 案例演示 添加test ...

  7. 使用jstl和el表达式来展示request域中存放的user对象的信息

    <%@ page import="java.util.ArrayList" %><%@ page import="java.util.List" ...

  8. pipeline是什么?

    目录 一.pipeline是什么? 二.jenkinsfile是什么 三.pipeline语法选择 四.脚本式和声明式 五.插件与pipeline 一.pipeline是什么? pipeline是部署 ...

  9. 分布式可扩展web体系结构设计实例分析

    Web分布式系统设计准则 下面以一个上传和查询图片的例子来说明分布式web结构的设计考虑和常用的提高性能的方法.该例子提供上传图片和下载图片两个简单功能,并且有一下假设条件?: - 可以存储无上限数量 ...

  10. 爆款预订,2022 年最值得关注的后台框架 —— Fantastic-admin

    前言 如果 2021 年你还没有听说过 Fantastic-admin ,那即将到来的 2022 年可不要再错过了. Fantastic-admin 做为一款开箱即用的 Vue 中后台管理系统框架,距 ...