Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)
前言
本文主要讲解 数据结构中的图 结构,包括 深度优先搜索(DFS)、广度优先搜索(BFS)、最小生成树算法等,希望你们会喜欢。
目录
1. 简介
- 数据结构的中属于 圆形结构 的逻辑结构
- 具体介绍如下
2. 基础概念
- 在图的数据结构中,有许多基础概念,如 边类型、图顶点 & 边间的关系等等
- 具体请看下图
3. 类型
图的类型分为很多种,具体如下:
3.1 有向图 & 无向图
3.2 连通图
定义
图中任意顶点都是连通的图具体相关概念
对于有向图 & 无向图,连通图的的具体概念又不同,具体如下
对于无向图:
对于有向图:
3.3 其余类型图
4. 存储结构
图的存储结构共有5种,具体请看下图
5. 图的遍历
数据结构:图文详解二叉树(遍历、类型、操作)
5.1 定义
从图中某1顶点出发,遍历图中其余所有顶点 & 使每1个顶点仅被访问1次
5.2 遍历方式
深度优先遍历(DFS
)、广度优先遍历(BFS
)
5.3 具体介绍
5.3.1 深度优先遍历( DFS )
- 简介
- 算法示意图
- 具体实现:递归 & 非递归
此处图的存储结构 = 邻接矩阵
import java.util.Stack;public class MyGraph {/*** 准备工作1:设置变量*/private int vexnum; // 存放图中顶点数量private char[] vertices; // 存放结点数据private int [][] arcs; // 存放图的所有边private boolean[] visited;// 记录节点是否已被遍历/*** 准备工作2:初始化图的顶点数量、数据 & 边*/public MyGraph(int n){vexnum = n;vertices = new char[n];visited = new boolean[n];arcs = new int[n][n];}/*** 图的深度优先遍历算法实现:递归* 类似 前序遍历*/public void DFSTraverse(){// 1. 初始化所有顶点的访问标记// 即,都是未访问状态for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 深度优先遍历顶点(从未被访问的顶点开始)for(int i=0;i < vexnum;i++){if(visited[i]==false){// 若是连通图,只会执行一次traverse(i); // ->>看关注1}}}/*** 关注1:邻接矩阵的深度优先搜索递归算法* 即,从第i个顶点开始深度优先遍历*/private void traverse(int i){// 1. 标记第i个顶点已遍历visited[i] = true;// 2. (输出)访问当前遍历的顶点visit(i);// 3. 遍历邻接矩阵中第i个顶点的所有邻接顶点for(int j=0;j<vexnum;j++){// a. 若当前顶点的邻接顶点存在 & 未被访问,则递归 深度优先搜索 算法if(arcs[i][j]==1 && visited[j]==false){// b. 将当前顶点的邻接顶点作为当前顶点,递归 深度优先搜索 算法traverse(j);}}}/*** 辅助算法1:访问顶点值*/public void visit(int i){System.out.print(vertices[i] + " ");}/*** 图的深度优先遍历算法实现:非递归* 原理:采用 栈实现*/public void DFSTraverse2(){// 1. 初始化顶点访问标记// 全部标记为:未访问for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 创建栈Stack<Integer> s = new Stack<Integer>();for(int i=0 ; i<vexnum; i++){// 3. 若该顶点未被访问if(!visited[i]){// 4. 入栈该顶点s.add(i);do{// 出栈int curr = s.pop();// 如果该节点还没有被遍历,则遍历该节点并将子节点入栈if(visited[curr]==false){// 遍历并打印visit(curr);visited[curr] = true;// 没遍历的子节点入栈for(int j=vexnum-1; j>=0 ; j-- ){if(arcs[curr][j]==1 && visited[j]==false){s.add(j);}}}}while(!s.isEmpty());}}}/*** 测试(递归 & 非递归)*/public static void main(String[] args) {// 1. 初始化图的结构(顶点数量 = 9)MyGraph g = new MyGraph(9);// 2. 设置顶点数据char[] vertices = {'A','B','C','D','E','F','G','H','I'};g.setVertices(vertices);// 3. 设置边g.addEdge(0, 1);g.addEdge(0, 5);g.addEdge(1, 0);g.addEdge(1, 2);g.addEdge(1, 6);g.addEdge(1, 8);g.addEdge(2, 1);g.addEdge(2, 3);g.addEdge(2, 8);g.addEdge(3, 2);g.addEdge(3, 4);g.addEdge(3, 6);g.addEdge(3, 7);g.addEdge(3, 8);g.addEdge(4, 3);g.addEdge(4, 5);g.addEdge(4, 7);g.addEdge(5, 0);g.addEdge(5, 4);g.addEdge(5, 6);g.addEdge(6, 1);g.addEdge(6, 3);g.addEdge(6, 5);g.addEdge(6, 7);g.addEdge(7, 3);g.addEdge(7, 4);g.addEdge(7, 6);g.addEdge(8, 1);g.addEdge(8, 2);g.addEdge(8, 3);// 4. 执行 图的深度优先遍历:(递归 & 非递归)System.out.println("深度优先遍历(递归)");g.DFSTraverse();System.out.println("深度优先遍历(非递归)");g.DFSTraverse2();}/*** 辅助算法1:添加边(无向图)*/public void addEdge(int i, int j) {// 边的头尾不能为同一节点if (i == j) return;// 将邻接矩阵的第i行第j列的元素值置为1;arcs[i][j] = 1;// 将邻接矩阵的第j行第i列的元素值置为1;arcs[j][i] = 1;// 设置为1代表2顶点之间存在 边 (设置相等原因 = 邻接矩阵 是对称的)}/*** 辅助算法2:设置顶点数据*/public void setVertices(char[] vertices) {this.vertices = vertices;}
}
- 测试结果
深度优先遍历(递归)
A B C D E F G H I
深度优先遍历(非递归)
A B C D E F G H I
特别注意
对于图的存储结构 = 邻接表实现,只需要将 存储边 的2维数组 改成链表即可。图的存储结构 = 邻接矩阵 / 邻接表 的性能对比
5.3.2 广度优先遍历(BFS)
- 简介
- 算法示意图
- 具体流程
注:
G
比I
先访问的原因 = 用数组存储顶点时,G
的下标 比I
的下标小(按ABCDEFGHI
顺序存储)
- 具体实现
非递归:采用队列
import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;public class MyGraph {/*** 准备工作1:设置变量*/private int vexnum; // 存放图中顶点数量private char[] vertices; // 存放结点数据private int [][] arcs; // 存放图的所有边private boolean[] visited;// 记录节点是否已被遍历/*** 准备工作2:初始化图的顶点数量、数据 & 边*/public MyGraph(int n){vexnum = n;vertices = new char[n];visited = new boolean[n];arcs = new int[n][n];}/*** 广度优先搜索 算法实现* 原理:非递归(采用队列)*/public void BFS(){// 1. 初始化所有顶点的访问标记// 即,设置为未访问状态for (int i = 0; i < vexnum; i++) {visited[i] = false;}// 2. 创建队列Queue<Integer> q=new LinkedList<Integer>();// 3. 对所有顶点做遍历循环(从第1个顶点开始)// 若遍历完毕,则结束整个层序遍历for(int i=0;i < vexnum;i++){// 4. 若当前顶点未被访问,就进行处理// 若当前顶点已被访问,则回到3进行判断if( visited[i]==false ) {// 5. (输出)访问当前顶点visit(i);// 6. 标记当前顶点已被访问visited[i] = true;// 7. 入队当前顶点q.add(i);// 8.判断当前队列是否为空// 若为空则跳出循环,回到3进行判断while(!q.isEmpty()) {// 9. 出队队首元素 & 将出队的元素 赋值为 当前顶点i = q.poll();// 10. 遍历当前顶点的所有邻接点// 若遍历完毕,则回到8判断for(int j=0; j< vexnum ; j++){// 11. 若当前顶点的邻接顶点存在 & 未被访问,则执行处理// 否则回到10判断if(arcs[i][j]==1 && visited[j]==false){// 12. (输出)访问当前顶点的邻接顶点visit(j);// 13. 标记当前顶点的邻接顶点已被访问visited[j] = true;// 14. 入队当前顶点的邻接顶点q.add(j);}}}}}}/*** 辅助算法1:访问该顶点*/public void visit(int i){System.out.print(vertices[i] + " ");}/** * 测试算法*/public static void main(String[] args) {// 1. 初始化图的结构(顶点数量 = 9MyGraph g = new MyGraph(9);// 2. 设置顶点数据char[] vertices = {'A','B','C','D','E','F','G','H','I'};g.setVertices(vertices);// 3. 设置边g.addEdge(0, 1);g.addEdge(0, 5);g.addEdge(1, 0);g.addEdge(1, 2);g.addEdge(1, 6);g.addEdge(1, 8);g.addEdge(2, 1);g.addEdge(2, 3);g.addEdge(2, 8);g.addEdge(3, 2);g.addEdge(3, 4);g.addEdge(3, 6);g.addEdge(3, 7);g.addEdge(3, 8);g.addEdge(4, 3);g.addEdge(4, 5);g.addEdge(4, 7);g.addEdge(5, 0);g.addEdge(5, 4);g.addEdge(5, 6);g.addEdge(6, 1);g.addEdge(6, 3);g.addEdge(6, 5);g.addEdge(6, 7);g.addEdge(7, 3);g.addEdge(7, 4);g.addEdge(7, 6);g.addEdge(8, 1);g.addEdge(8, 2);g.addEdge(8, 3);// 4. 执行 图的广度优先遍历(非递归)System.out.print("广度优先遍历(非递归):");g.BFS();}/*** 辅助算法1:添加边(无向图)*/public void addEdge(int i, int j) {// 边的头尾不能为同一节点if (i == j) return;// 将邻接矩阵的第i行第j列的元素值置为1;arcs[i][j] = 1;// 将邻接矩阵的第j行第i列的元素值置为1;arcs[j][i] = 1;// 设置为1代表2顶点之间存在 边 (设置相等原因 = 邻接矩阵 是对称的)}/*** 辅助算法2:设置顶点数据*/public void setVertices(char[] vertices) {this.vertices = vertices;}}
- 执行结果
广度优先遍历(非递归):A B F C G I E D H
5.4 遍历方式对比
6. 最小生成树
本节主要讲解 图中的 最小生成树
6.1 定义
构造 连通网图 的最小成本生成树
- 网图:带有权值的图
- 最小成本:用(n-1)条边将 含n个顶点的连通图 连接起来 & 使得权值和最小
6.2 寻找最小生成树的算法
- 主要包括:(
Prim
)普利姆算法 & (Kruskal
)克鲁斯卡尔算法 - 具体介绍如下
6.2.1 (Prim)普利姆算法
- 算法概述
- 算法原理流程示意图
- 举例说明
6.2.2(Kruskal)克鲁斯卡尔算法
- 算法概述
6.3 算法对比
7. 最短路径
7.1 定义
- 对于非网图(无权值),最短路径 = 两顶点间经过的边数最少的路径
- 对于网图(有权值):最短路径 = 两顶点间经过的边上权值和最少的路径
第1个顶点 = 源点、第2个顶点 = 终点
7.2 需解决的问题
从源点 -> 其余各顶点的最短路径
7.3 寻找最短路径 算法
主要包括:迪杰斯特拉算法
(Dijkstra)
、弗洛伊德算法(Floyd)
具体介绍如下
8. 总结
- 本文主要讲解了数据结构中的图
- 下面我将继续对 数据结构,有兴趣可以继续关注Carson_Ho的安卓开发笔记
帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!
Carson带你学数据结构:手把手带你了解 ”图“ 所有知识!(含DFS、BFS)相关推荐
- 【数据结构与算法分析】0基础带你学数据结构与算法分析09--线索二叉树 (TBT)
目录 TBT 的存储结构 线索化 如果一棵二叉树,所有原本为空的右孩子改为指向该结点的中序遍历的后继,所有原本为空的左孩子改为指向该结点的中序遍历的前驱,那么修改后的二叉树被称为 线索二叉树 (Thr ...
- 数据结构实验之图论三:判断可达性(dfs/bfs)
Description 在古老的魔兽传说中,有两个军团,一个叫天灾,一个叫近卫.在他们所在的地域,有n个隘口,编号为1-n,某些隘口之间是有通道连接的.其中近卫军团在1号隘口,天灾军团在n号隘口.某一 ...
- Carson带你学Android:RxJava过滤操作符
前言 Rxjava由于其基于事件流的链式调用.逻辑简洁 & 使用简单的特点,深受各大 Android开发者的欢迎. 今天,我将为大家详细介绍RxJava操作符中最常用的 过滤操作符,希望你们会 ...
- Carson带你学Android:图文详解RxJava背压策略
前言 Rxjava,由于其基于事件流的链式调用.逻辑简洁 & 使用简单的特点,深受各大 Android开发者的欢迎. 本文主要讲解的是RxJava中的 背压控制策略,希望你们会喜欢. Cars ...
- 最后一期:如何更新LSTM模型?(附代码)| 博士带你学LSTM
最后一期:如何更新LSTM模型?(附代码)| 博士带你学LSTM LSTM是一种时间递归神经网络,适合于处理和预测时间序列中间隔和延迟相对较长的重要事件.在自然语言处理.语言识别等一系列的应用上都取得 ...
- python基础入门(Peak带你学python)
带你学python Peak带你学python 基础语法知识 print函数 转义字符 进制转换 保留字和标识符 变量 数据类型 数据类型转换 注释 input函数 运算符 运算符优先级 布尔值 if ...
- Carson带你学Android:手把手带你入门跨平台UI开发框架Flutter
前言 Flutter 作为Google出品的一个新兴的跨平台移动客户端UI开发框架,正在被越来越多的开发者和组织使用,包括阿里的咸鱼.腾讯的微信等. 今天,我将献上一份 <全面 & 详细 ...
- Carson带你学Android:请收好这一份全面详细的Android学习指南
前言 如果你也学习Android,那么你大概率会看过我的文章.经常有读者给我留言:"该怎么学习Android?"."日常学习Android的方法是什么". 今天 ...
- Carson带你学Android:你要的WebView与 JS 交互方式都在这里了
前言 现在很多App里都内置了Web网页(Hybrid App),比如说很多电商平台,淘宝.京东.聚划算等等,如下图 上述功能是由Android的WebView实现的,其中涉及到Android客户端与 ...
最新文章
- unix 查询进程并中止
- (原創) 將map輸出到cout,是否有更方便的方法? (C/C++) (STL)
- Tensorflow学习: 乘法demo
- 如何使用来电盒--宇然电脑公司管理软件
- tomcat手动发布
- vcf文件(call variants得来的)怎么看变异是纯合还是杂合的
- python中dic_python之dic {字典}(重要指数*****)
- C# 获取CPU序列号、MAC地址、硬盘ID等系统信息
- VS2005 工程在win7下使用管理员权限运行
- 接口交互时的URL带有日期格式的参数,注意拼装地址中的特殊符号(如空格等)
- python做算法题_Python 笔试常见基础算法题
- 程序猿趣图几张,第一张就亮瞎了..
- ECharts 前端数据可视化
- 今天身边的一个好友想的一个点子,可能会颠覆传统电商网,称为线上商店和线下商店的最后收割者
- 无卷积!金字塔视觉Transformer(PVT):用于密集预测的多功能backbone
- 【HTTP图片服务器】【项目记录2】:安装、配置MySQL环境
- 2012-2013年世界大学计算机专业排名
- Mac M1芯片安装 MySQL
- 软件项目管理第二篇:项目计划 (1)——范围计划
- 做出刷屏文字的6大哲学