广度优先和深度优先
1 package test; 2 3 import java.util.ArrayList; 4 import java.util.LinkedList; 5 6 public class Graph { 7 private int[][] edges; 8 private ArrayList vertexList; 9 private boolean[] isVisited; 10 private int numVertex; 11 12 public Graph(int n) { 13 edges = new int[n][n]; 14 vertexList = new ArrayList(n); 15 isVisited = new boolean[n]; 16 numVertex = n;
17 // 每个顶点初始化为false 18 for (int i = 0; i < n; i++) { 19 isVisited[i] = false; 20 } 21 } 22 23 public void insertVertex(String vertex) { 24 vertexList.add(vertex); 25 } 26 27 public void insertEdge(int v1, int v2, int weight) { 28 edges[v1][v2] = weight; 29 } 30 31 public void depthFirstSearch() { 32 for (int i = 0; i < numVertex; i++) { 33 if (!isVisited[i])// 如果某个点没访问的话,那么去遍历 34 { 35 depthFirstSearch(isVisited, i); 36 } 37 } 38 } 39 40 public int getFirstNeighbor(int index) { 41 for (int j = 0; j < numVertex; j++) { 42 if (edges[index][j] > 0) { 43 return j; 44 } 45 } 46 return -1;// 表示此点没有与之相邻的点 47 } 48 49 // 从v2开始检查,可以减少计算量 50 public int getNextNeighbor(int v1, int v2) { 51 for (int j = v2 + 1; j < numVertex; j++) { 52 if (edges[v1][j] > 0) { 53 return j; 54 } 55 } 56 return -1; 57 } 58 59 public void depthFirstSearch(boolean[] isVisited, int i) { 60 // 61 System.out.print(vertexList.get(i) + " "); 62 isVisited[i] = true; 63 int newStartNode = getFirstNeighbor(i); 64 while (newStartNode != -1) { 65 if (!isVisited[newStartNode])// 如果这个点没有被访问 66 { 67 depthFirstSearch(isVisited, newStartNode); 68 } 69 newStartNode = getNextNeighbor(i, newStartNode); 70 } 71 } 72 73 // 广度优先 74 public void broadFirstSearch() { 75 for (int i = 0; i < numVertex; i++) { 76 broadFirstSearch(isVisited, i); 77 } 78 } 79 80 private void broadFirstSearch(boolean[] isVisited, int i) { 81 int currentVisitedNode, adjacentCurrentVisitedNode; 82 LinkedList queue = new LinkedList(); 83 // 访问该节点 84 System.out.print(vertexList.get(i) + " "); 85 isVisited[i] = true; 86 // 节点入队列 87 queue.add(i); 88 while (!queue.isEmpty()) { 89 currentVisitedNode = ((Integer) queue.removeFirst()).intValue();// 移除队列的第一个元素,即头 90 adjacentCurrentVisitedNode = getFirstNeighbor(currentVisitedNode); 91 92 while (adjacentCurrentVisitedNode != -1) { 93 if (!isVisited[adjacentCurrentVisitedNode]) { 94 // 访问该节点 95 System.out.print(vertexList.get(adjacentCurrentVisitedNode) + " "); 96 // 标记该节点 97 isVisited[adjacentCurrentVisitedNode] = true; 98 // 入队列 99 queue.addLast(adjacentCurrentVisitedNode); 100 } 101 // 寻找下一个邻接节点 102 adjacentCurrentVisitedNode = getNextNeighbor(currentVisitedNode, adjacentCurrentVisitedNode); 103 } 104 } 105 106 } 107 }
1 package test; 2 3 public class TestSearch { 4 public static void main(String args[]) { 5 6 String labels[] = { "1", "2", "3", "4", "5" };// 结点的标识 7 int n = labels.length; 8 Graph graph = new Graph(n); 9 for (String label : labels) { 10 graph.insertVertex(label);// 插入结点 11 } 12 // 插入四条边 13 graph.insertEdge(0, 1, 1); 14 graph.insertEdge(0, 2, 1); 15 graph.insertEdge(1, 3, 1); 16 graph.insertEdge(1, 4, 1); 17 18 System.out.println("深度优先搜索序列为:"); 19 graph.depthFirstSearch(); 20 21 System.out.println(); 22 System.out.println("广度优先搜索序列为:"); 23 graph.broadFirstSearch(); 24 } 25 }
运行结果如下:
深度优先搜索序列为:
1 2 4 5 3
广度优先搜索序列为:
1 2 3 4 5
总的来说,BFS多用于寻找最短路径的问题,DFS多用于快速发现底部节点。
DFS的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底。
DFS适合此类题目:给定初始状态跟目标状态,要求判断从初始状态到目标状态是否有解。
深度与广度的比较:
我们搜索一个图是按照树的层次来搜索的。
我们假设一个节点衍生出来的相邻节点平均的个数是N个,那么当起点开始搜索的时候,队列有一个节点,当起点拿出来后,把它相邻的节点放进去,那么队列就有N个节点,当下一层
的搜索中再加入元素到队列的时候,节点数达到了N/2,你可以想象,一旦N是一个比较大的数的时候,这个树的层次又比较深,那这个队列就需要很大的内存空间了。
于是广度优先搜索的缺点出来了:在树的层次较深或子节点数较多的情况下,消耗内存十分严重。广度优先搜索适用于节点的子节点数量不多,并且树的层次不会太深的情况。
那么深度优先就可以克服这个缺点,因为每次搜的过程,每一层只需维护一个节点。但回过头想想,广度优先能够找到最短路径,那深度优先能否找到呢?深度优先的方法是一条路走到黑,
那显然无法知道这条路是不是最短的,所以你还得继续走别的路去判断是否是最短路?
于是深度优先搜索的缺点也出来了:难以寻找最优解,只能寻找有解。其优点就是内存消耗小,克服了刚刚说的广度优先搜索的缺点。
递归版深度优先
1 public class DFSTest 2 { 3 private int vertexNum;//顶点个数 4 private int[][] adjacent;//邻接矩阵 5 private boolean[] isVisited;//顶点是否经过的标记 6 private char[] nodeName;//顶点名称 7 8 //图的初始化 9 public DFSTest(int n) 10 { 11 vertexNum=n; 12 adjacent=new int[vertexNum][vertexNum]; 13 isVisited=new boolean[vertexNum]; 14 for (int i=0;i<vertexNum;i++) 15 { 16 for (int j=0;j<vertexNum;j++) 17 { 18 adjacent[i][j]=0; 19 } 20 } 21 22 for (int k=0;k<n;k++) 23 { 24 isVisited[k]=false; 25 } 26 } 27 28 public void setNodeName(char[] a) 29 { 30 nodeName=new char[vertexNum]; 31 for (int i=0;i<a.length;i++) 32 { 33 nodeName[i]=a[i]; 34 } 35 } 36 37 //无向图 38 public void setAdjacent(int i,int j) 39 { 40 adjacent[i][j]=1; 41 adjacent[j][i]=1; 42 } 43 44 45 46 //遍历 47 public void dfsTraverse() 48 { 49 for (int i=0;i<vertexNum;i++) 50 { 51 if (isVisited[i]==false) 52 { 53 dfs(i); 54 } 55 } 56 } 57 58 public void dfs(int n) 59 { 60 isVisited[n]=true;// 61 //可利用每次遍历的节点的个数来判断此路是否有解 62 System.out.println(nodeName[n]); 63 for (int i=0;i<vertexNum;i++) 64 { 65 if (adjacent[n][i]==1&&isVisited[i]==false) 66 { 67 dfs(i); 68 } 69 } 70 } 71 72 73 public static void main(String[] args) 74 { 75 int n=4;//表示图中顶点的个数 76 DFSTest dfsTest=new DFSTest(n); 77 //给顶点赋名字 78 char[] name={'A','B','C','D'}; 79 dfsTest.setNodeName(name); 80 //加边 81 dfsTest.setAdjacent(0, 1); 82 dfsTest.setAdjacent(1, 3); 83 dfsTest.setAdjacent(2, 3); 84 85 dfsTest.dfsTraverse(); 86 87 } 88 }
路漫漫其修远兮,吾将上下而求索