一、图的基本介绍

1、为什么要有图

QQ截图20200902150009.jpg

2、图的举例说明

图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。 结点也可以称为
顶点。如图:
QQ截图20200902150125.jpg

3、图的常用概念

QQ截图20200902150202.jpg

4、图的表现方式

邻接矩阵

QQ截图20200902150258.jpg

邻接表

QQ截图20200902150306.jpg

5、代码实现(邻接矩阵)

QQ截图20200903120931.jpg
依据上图进行插入各个节点以及边。
属性有三个组成:vertexs(用来存储节点名称,集合存储)、edges(用来存储邻接矩阵,二维数组)、numOfEdge(用来存储两节点相连的实际边数,int型)
主要方法有三个:构造方法(根据输入节点个数来为三个属性进行赋初值)、插入节点名称(集合插入)、插入邻接矩阵中两点连线的权值
其他常用普遍方法:

  1. package graph;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. public class Graph {
  5. public static void main(String[] args) {
  6. Graph graph = new Graph(5);
  7. String[] vertexName = {"A","B","C","D","E"};
  8. //插入节点名称
  9. for(String vertex:vertexName) {
  10. graph.insertVertex(vertex);
  11. }
  12. //插入各自节点的权值
  13. //A-B A-C B-C B-D B-E
  14. graph.insertEdge(0, 1, 1);
  15. graph.insertEdge(0,2,1);
  16. graph.insertEdge(1,2,1);
  17. graph.insertEdge(1,3,1);
  18. graph.insertEdge(1,4,1);
  19. graph.showGraph();
  20. }
  21. private ArrayList<String> vertexs;//用来存储节点的
  22. private int[][] edges;//存储图对应的邻接矩阵
  23. private int numOfEdge;//用来存储边的数量
  24. //构造函数,用来对属性进行初始化
  25. public Graph(int n) {
  26. edges = new int[n][n];//邻接矩阵进行初始化
  27. vertexs = new ArrayList<>(n);//开辟指定空间的集合
  28. numOfEdge = 0;//边的数目暂时不确定
  29. }
  30. //其他普遍常用的方法*************
  31. //获取边数
  32. public int getNumOfEdge(){
  33. return numOfEdge;
  34. }
  35. //获取指定下标的对应类型名称
  36. public String getValueByIndex(int i) {
  37. return vertexs.get(i);
  38. }
  39. //获取两点之间的权值
  40. public int getWeight(int v1,int v2) {
  41. return edges[v1][v2];
  42. }
  43. //显示图对应的矩阵
  44. public void showGraph() {
  45. for(int[] link:edges) {
  46. System.out.println(Arrays.toString(link));
  47. }
  48. }
  49. //其他普遍常用的方法*************
  50. //插入节点
  51. public void insertVertex(String vertex) {
  52. vertexs.add(vertex);//来进行插入到list集合中
  53. }
  54. //插入边
  55. public void insertEdge(int v1,int v2,int weight) {
  56. edges[v1][v2] = weight;
  57. edges[v2][v1] = weight;
  58. numOfEdge++;
  59. }
  60. }

运行结果:
QQ截图20200903142140.jpg


二、图的遍历

所谓图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种 访问策略:(1)深度优先遍历 (2)广度优先遍历

1、深度优先遍历(dfs)

源代码:Graph.java

基本思想以及算法步骤

QQ截图20200904121437.jpg
算法步骤
QQ截图20200904121521.jpg

代码实现以及思路

QQ截图20200903154107.jpg

进行深搜需要有两个辅助方法:都很简单只不过就是遍历看是否大于1返回对应下标
① 获取当前节点位置行中第一个相连接的节点 —》 getFirstConnectNode
② 获取到当前节点位置中对应位置节点之后的一个节点 —》getNextConnectNode
下面dfs方法分成两部分,实际上可以直接分成一部分,只不过更加区分好理解而已。

  1. package graph;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. public class Graph {
  5. private ArrayList<String> vertexs;//用来存储节点的
  6. private int[][] edges;//存储图对应的邻接矩阵
  7. private int numOfEdge;//用来存储边的数量
  8. private boolean[] isVisible;//用来判断该点是否已经访问过了
  9. //构造函数,用来对属性进行初始化
  10. public Graph(int n) {
  11. edges = new int[n][n];//邻接矩阵进行初始化
  12. vertexs = new ArrayList<>(n);//开辟指定空间的集合
  13. numOfEdge = 0;//边的数目暂时不确定
  14. isVisible = new boolean[n];//默认都是false
  15. }
  16. //dfs算法相关******************
  17. //辅助方法1:获取对应节点中第一个连接的节点
  18. public int getFirstConnectNode(int w) {
  19. for(int i = 0;i<vertexs.size();i++) {
  20. if(edges[w][i]>0) {
  21. return i;//返回第一个连接的节点位置
  22. }
  23. }
  24. return -1;//没有的话就返回-1;
  25. }
  26. //辅助方法2:获取对应节点中的下一个节点,需要传入两个值一个是当前节点,另一个是当前节点这行中的位置
  27. public int getNextConnectNode(int w1,int w2) {
  28. for(int i = w2+1;i<vertexs.size();i++) {
  29. //找到w2节点之后的进行返回对应位置
  30. if(edges[w1][i]>0) {
  31. return i;
  32. }
  33. }
  34. return -1;
  35. }
  36. //开始写dfs算法
  37. private void dfs(int n) {
  38. //获取当前访问的节点
  39. System.out.printf(vertexs.get(n)+"->");
  40. //当前节点已经被访问
  41. isVisible[n] = true;
  42. //获取到对应节点连接的第一个节点
  43. int w = getFirstConnectNode(n);
  44. while(w != -1) {
  45. if(!isVisible[w]) {
  46. dfs(w);
  47. }
  48. w = getNextConnectNode(n, w);//当前节点为n,对应位置节点为w
  49. }
  50. }
  51. //这里写一个外部,涉及到所有可能性
  52. public void dfs() {
  53. //对每个边都进行搜查,而不是只是一个节点不断往下搜查
  54. for(int i=0;i<vertexs.size();i++) {
  55. if(!isVisible[i]) {
  56. dfs(i);
  57. }
  58. }
  59. }
  60. //dfs算法相关******************
  61. ....其他方法可见上面图的代码实现
  62. }

测试方法:

  1. package graph;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. public class Graph {
  5. public static void main(String[] args) {
  6. Graph graph = new Graph(5);
  7. String[] vertexName = {"A","B","C","D","E"};
  8. //插入节点名称
  9. for(String vertex:vertexName) {
  10. graph.insertVertex(vertex);
  11. }
  12. //插入各自节点的权值
  13. //A-B A-C B-C B-D B-E
  14. graph.insertEdge(0, 1, 1);
  15. graph.insertEdge(0,2,1);
  16. graph.insertEdge(1,2,1);
  17. graph.insertEdge(1,3,1);
  18. graph.insertEdge(1,4,1);
  19. //展示矩形图
  20. graph.showGraph();
  21. //进行深搜遍历
  22. graph.dfs();
  23. }
  24. }

运行结果:
QQ截图20200903154245.jpg

2、广度优先遍历(BFS)

源代码:Graph.java

基本思想以及算法步骤

QQ截图20200904121614.jpg
算法步骤
QQ截图20200904121643.jpg
QQ截图20200904121648.jpg

代码实现

QQ截图20200904121740.jpg

  1. //辅助方法1:获取对应节点中第一个连接的节点
  2. public int getFirstConnectNode(int w) {
  3. for(int i = 0;i<vertexs.size();i++) {
  4. if(edges[w][i]>0) {
  5. return i;//返回第一个连接的节点位置
  6. }
  7. }
  8. return -1;//没有的话就返回-1;
  9. }
  10. //辅助方法2:获取对应节点中的下一个节点,需要传入两个值一个是当前节点,另一个是当前节点这行中的位置
  11. public int getNextConnectNode(int w1,int w2) {
  12. for(int i = w2+1;i<vertexs.size();i++) {
  13. //找到w2节点之后的进行返回对应位置
  14. if(edges[w1][i]>0) {
  15. return i;
  16. }
  17. }
  18. return -1;
  19. }
  20. //开始写bfs算法
  21. public void bfs(int n) {
  22. int u;//设置为队列的头节点对应下标
  23. int w;//邻接节点
  24. System.out.print(vertexs.get(n)+"=>");
  25. //创建一个队列
  26. LinkedList<Integer> list = new LinkedList<>();
  27. //设置对应节点已经访问过
  28. isVisible[n] = true;
  29. list.addLast(n);//将首先这个节点放置最后
  30. //1、队列不为空为判断情况 这里相当于将每一行的放置在队列中依次取出
  31. while(!list.isEmpty()) {
  32. u = list.removeFirst();//取出队列中的第一个节点并获取下标
  33. //2、得到第一个邻接节点的下标w 这里相当于遍历头节点行进行打印以及放置队列
  34. w = getFirstConnectNode(u);
  35. while(w != -1) {
  36. if(!isVisible[w]) {
  37. System.out.print(vertexs.get(w)+"=>");
  38. isVisible[w] = true;
  39. list.addLast(w);//将该节点添加到队列中
  40. }
  41. w = getNextConnectNode(u, w);//继续获取头节点对应下一节点的位置
  42. }
  43. }
  44. }
  45. //对所有情况进行遍历
  46. public void bfs() {
  47. for(int i = 0;i<vertexs.size();i++) {
  48. if(!isVisible[i]) {
  49. bfs(i);
  50. }
  51. }
  52. }


测试代码:**

  1. package graph;
  2. import java.util.ArrayList;
  3. import java.util.Arrays;
  4. import java.util.LinkedList;
  5. public class Graph {
  6. public static void main(String[] args) {
  7. Graph graph = new Graph(5);
  8. String[] vertexName = {"A","B","C","D","E"};
  9. //插入节点名称
  10. for(String vertex:vertexName) {
  11. graph.insertVertex(vertex);
  12. }
  13. //插入各自节点的权值
  14. //A-B A-C B-C B-D B-E
  15. graph.insertEdge(0, 1, 1);
  16. graph.insertEdge(0,2,1);
  17. graph.insertEdge(1,2,1);
  18. graph.insertEdge(1,3,1);
  19. graph.insertEdge(1,4,1);
  20. //展示矩形图
  21. graph.showGraph();
  22. //进行深搜遍历
  23. //graph.dfs();
  24. //进行广搜遍历
  25. graph.bfs();
  26. }
  27. }

运行结果:
QQ截图20200904122021.jpg

图的深度优先 VS 广度优先

QQ截图20200904175303.jpg


整理者:长路 时间:2020/9/2-2020/9/4