图的创建和遍历
1:图的常用的数据结构
1.1:邻接矩阵
通过数组二维举证的方式来存放图的数据结构,这也是最简单原始的方法来表示图的结构。
所有的顶点数组
node1 |
node2 |
node3 |
node4 |
node5 |
以及顶点举证
node1 | node2 | node3 | node4 | node5 | |
node1 | 0 | 1 | 0 | 1 | 0 |
node2 | 1 | 0 | 0 | 0 | 0 |
node3 | 0 | 0 | 0 | 0 | 1 |
node4 | 1 | 0 | 0 | 0 | 0 |
node5 | 0 | 0 | 1 | 0 | 0 |
矩阵为1的位置,代表所在位置两个顶点的是相连的。
矩阵为0的位置,代表所在位置两个顶点的是不相连的。
这种方式直观,简单,但是当顶点较多,且边较少的时,则会有大量的存储位置的浪费!
1.2:邻接表
邻接表是通过数组和链表的方式来实现的,为所有顶点创建一个数组,然后两顶点之间如果有边相连,则在每个顶点后面链上相连的顶点即可!可以解决有邻接矩阵方式造成的大量的空白内存。
node1--> node2 --> node5 |
node2 --> node1 --> node3 |
node3 --> node2 -->node4 |
node4 -->node3 |
node5 --> node1 |
2:常用的遍历方式
2.1:depth first search
2.1.1:基于栈的搜索方式
本例子中采用stack来实现深度搜寻!因为,stack是先进后出,所以可以达到将当前搜索路径都保存进去,如果没有了可达的其他节点,则弹出当前节点,在下一个节点中重复搜索直达把所有节点都搜索完成!!!但是既然使用了stack不可避免地速度下降,
package Graph; import java.util.Stack; /** * @author :dazhu * @date :Created in 2020/3/13 19:04 * @description:图的学习 * @modified By: * @version: $ */ public class Main { public static void main(String[] args){ Graph g = new Graph(); g.addVertex('A'); g.addVertex('B'); g.addVertex('C'); g.addVertex('D'); g.addVertex('E'); g.addEdge(0,1); g.addEdge(1,2); g.addEdge(0,3); g.addEdge(3,4); g.addEdge(2,4); g.dfs(); } } class Graph { private final int MAX_VERTS = 20; private Vertex vertexList[]; private int adjMat[][]; private int nVerts; private Stack<Integer> theStack = new Stack<Integer>(); public Graph(){ //初始化,顶点数组 this.vertexList = new Vertex[MAX_VERTS]; //创建边矩阵 this.adjMat = new int[MAX_VERTS][MAX_VERTS]; this.nVerts = 0; //初始化边矩阵 for(int i=0;i<MAX_VERTS;i++){ for(int j=0;j<MAX_VERTS;j++){ adjMat[i][j] = 0; } } } //添加顶点到顶点数组 public void addVertex(char lab){ vertexList[nVerts++] = new Vertex(lab); } //添加边到边矩阵 public void addEdge(int start,int end){ adjMat[start][end] = 1; adjMat[end][start] = 1; } //显示所有顶点 public void showVertexs(){ for(int i=0;i<vertexList.length;i++){ System.out.println(vertexList[i]); } } //打印指定顶点 public void printVertex(int v){ System.out.println(vertexList[v].label); } //depth first search,深度优先搜索(使用stack来作为辅助) //原来分析 //第一步;首先取出一个顶点,以0号顶点为例子,加入stack中, //第二步;只要stack不为empty就一直循环,从stack得到顶点(但是不取走), //判断其有没有其他相连的顶点,若有则mark以下,在推入stack中。 //第三步:重读第二步,直到stack is empty即可! //第四步:reset all wasVisited flag!!! public void dfs(){ //begin at vertex 0 vertexList[0].wasVisited = true;//mark it printVertex(0);//print it theStack.push(0); while(!theStack.isEmpty()){//until stack empty int v = getAdjUnvisitedVertex(theStack.peek()); if(v==-1){//if there is no such vertex exiting theStack.pop(); } else{//if it exits vertexList[v].wasVisited = true;//mark it printVertex(v);//print it theStack.push(v);//push it } } //stack is empty ,so we have done for(int i=0;i<nVerts;i++){//reset flags vertexList[i].wasVisited = false; } } //返回该列中没有标记且为1的顶点 public int getAdjUnvisitedVertex(int v){ for(int j=0;j<nVerts;j++){ if(adjMat[v][j]==1&&vertexList[j].wasVisited==false){ return j;//return first such vertex } } return -1;//没有满足条件顶点,则返回-1. } //depth first search,深度优先搜索 } //顶点类的结构 //顶点可以存在数组中,用index来表示, //当然也可以存在链表中 class Vertex{ public char label;//label public boolean wasVisited;//是否遍历过的标记。 public Vertex(char label){ this.label = label; wasVisited = false; } }
2.1.2:迭代法的dfs搜索
//用递归的方法来进行深度搜索 //三要素: //当前顶点递归结束条件:当前顶点的相邻顶点都访问过。 //递归结束情况处理:退出当前顶点的递归,回到上个顶点。 //递归逻辑:继续向其相邻的顶点递归下去。 public void dfs(int v){ //在递去的过程中,显示本顶点信息。 printVertex(v);//display it vertexList[v].wasVisited= true;//mark it //获取当前顶点的相邻顶点 int l = getAdjUnvisitedVertex(v); //当前顶点没有相邻顶点,则退出本顶点的递归,回到上一个节点。 if(l == -1){ return ; } else{ //如果依然存在的话,则递归下去 dfs(l); } }
2.2:bearden first search
2.2.1:递归法的bfs
广度优先搜索:顾名思义相比于深度优先搜索,该方法优先搜索从出发顶点的所有相邻顶顶点,然后再搜索下一层的顶点,通过队列可以很容易实现该方法,一般通过queue的算法流程如下:
- 取出出发顶点如果该顶点没有标记则压入队列,并对所有相邻顶点进行遍历和访问标记,后压入队列!
- 当所有与相邻顶点入队列后,从尾部移除触发顶点。
- 取出队列,重复1-2的过程,直到队列为空,所有顶点都被标记。
借助队列的特性,先进先出可以实现广度搜索,但是也可通过迭代的方法来实现广度搜索!
迭代三要素如下:
- d
- d
- d