个人随笔
目录
图的深度优先搜索(DFS)和广度优先搜索(BFS):Java实现
2020-04-27 23:15:11

摘要:图的搜索,深度优先搜索,广度优先搜索,DFS,BFS,Java实现

一、背景知识:

(1)图的表示方法:邻接矩阵(二维数组)、邻接表(链表数组【链表的链表】)。

(2)图的搜索方法:深度优先搜索(DFS)和广度优先搜索(BFS)。

二、图的搜索:

1、深度优先搜索(DFS):

深度优先搜顾明思议是一条道走到黑,走到走不下去了再回过头来从新选择继续走,其实是回溯法的思想。我们这里用的技巧是,栈后进先出的性质刚好适用于深度优先搜索。步骤如下:

1、初始搜索点入栈,并标记它已搜索成功
2、如果栈不为空,则访问栈顶元素相邻的一个未标记为已搜索的顶点,若存在则标记它,并入栈
3、如果栈不为空,却不能执行2,则从栈中弹出一个顶点
4、如果不能执行2和3,则搜索结束

举个例子,比如上图的一个深度优先搜索,假设初始搜索点为A,栈为S。

第一步:A入栈,标记A已搜索,此时栈S={A}。搜索A成功。
第二步:栈不为空,访问A的一个未标记为已搜索相邻的的顶点,这里是B,此时S={A,B}。搜索成功B。
第三步:访问B的一个未标记为已搜索相邻的的顶点,这里是C,此时S={A,B,C}。搜索成功C。
第四步:访问C的一个未标记为已搜索相邻的的顶点,这里是E,此时S={A,B,C,E}。搜索成功E。
第五步:访问E的一个未标记为已搜索相邻的的顶点,这里是D,此时S={A,B,C,E,D}。搜索成功D。
第六步:访问D的一个未标记为已搜索相邻的的顶点,未找到,弹出D,此时S={A,B,C,E}。
第七步:访问E的一个未标记为已搜索相邻的的顶点,未找到,弹出E,此时S={A,B,C}。
第八步:访问C的一个未标记为已搜索相邻的的顶点,未找到,弹出C,此时S={A,B}。
第九步:访问B的一个未标记为已搜索相邻的的顶点,未找到,弹出B,此时S={A}。
第十步:访问A的一个未标记为已搜索相邻的的顶点,未找到,弹出A,此时S={}。
第十一步:栈为空,搜索结束。

搜索结果为:A,B,C,E,D

2、广度优先搜索

广度优先搜索,顾名思义就是先把旁边的都搜索完再向前进,其实这个是分支限界发的思想,我们这里用的技巧是队列,队列的先进先出的性质刚好适用于广度优先搜索。步骤如下:

1、初始搜索点入队列,并标记它已搜索成功
2、若队列不为空,则找到队头未标记为已搜索相邻的顶点,若存在,则标记为已搜索,继续执行这一步。
3、若2不能执行,则队头出对列,继续执行2.
4、若队列为空则搜索结束。

举个例子,比如上图的一个广度优先搜索,假设初始搜索点为A,队列为Q

第一步:A入队列,标记A已搜索,此时对队列为Q={A}。
第二步:则找到队头A未标记为已搜索相邻的顶点B,标记B为已搜索,此时队列为Q={A,B}。
第三步:继续找到队头A未标记为已搜索相邻的顶点C,标记C为已搜索,此时队列为Q={A,B,C}。
第四步:继续找到队头A未标记为已搜索相邻的顶点D,标记C为已搜索,此时队列为Q={A,B,C,D}。
第五步:继续找到队头A未标记为已搜索相邻的顶点,此时找不到,则对头出队列Q={B,C,D}。输出A。
第六步:继续找到队头B未标记为已搜索相邻的顶点,此时找不到,则对头出队列Q={C,D}。输出B。
第七步:继续找到队头C未标记为已搜索相邻的顶点D,标记C为已搜索,此时队列为Q={C,D,E}。
第八步:继续找到队头C未标记为已搜索相邻的顶点,此时找不到,则对头出队列Q={D,E}。输出C。
第九步:继续找到队头D未标记为已搜索相邻的顶点,此时找不到,则对头出队列Q={E}。输出D。
第十步:继续找到队头E未标记为已搜索相邻的顶点,此时找不到,则对头出队列Q={}。输出E。
第十一步:队列为空,搜索结束。

搜索结果为:A,B,C,D,E


三、Java实现

经过上面的分析,相信很容易就能够写出代码来了,我下面是自己封装了一个栈和一个队列,并且队列是静态的,用数组实现。

  1. /**
  2. * 深度优先搜索,广度优先搜索
  3. * @author suibibk@qq.com
  4. */
  5. public class DFS_BFS_SEARCH {
  6. public static void main(String[] args) {
  7. Graph graph = new Graph(5);
  8. graph.addVertex("A");
  9. graph.addVertex("B");
  10. graph.addVertex("C");
  11. graph.addVertex("D");
  12. graph.addVertex("E");
  13. graph.addEdge("A", "B");
  14. graph.addEdge("A", "C");
  15. graph.addEdge("A", "D");
  16. graph.addEdge("B", "C");
  17. graph.addEdge("C", "E");
  18. graph.addEdge("E", "D");
  19. graph.addEdge("C", "D");
  20. System.out.println("输出该图G:");
  21. graph.print();
  22. System.out.println("深度优先搜索从A开始:");
  23. graph.dfs("A");
  24. System.out.println();
  25. System.out.println("深度优先搜索从E开始:");
  26. graph.dfs("E");
  27. System.out.println();
  28. System.out.println("广度优先搜索从A开始:");
  29. graph.bfs("A");
  30. System.out.println();
  31. System.out.println("广度优先搜索从E开始:");
  32. graph.bfs("E");
  33. }
  34. }
  35. //定义图:无向图
  36. class Graph{
  37. //顶点数目
  38. int size;
  39. //顶点
  40. List<String> vertexs;
  41. //边
  42. int[][] edges;
  43. //被访问的接地那,0是未被访问,1是已被访问
  44. int[] visited;
  45. //初始化
  46. public Graph(int size) {
  47. this.size=size;
  48. this.vertexs=new ArrayList<String>(size);
  49. this.edges=new int[size][size];
  50. this.visited=new int[size];
  51. }
  52. //添加顶点
  53. public void addVertex(String vertex) {
  54. vertexs.add(vertex);
  55. }
  56. //添加边
  57. public void addEdge(String vertex1,String vertex2) {
  58. int index1 = this.getPosition(vertex1);
  59. if(index1==-1) {
  60. System.out.println("顶点"+vertex1+"不存在");
  61. return;
  62. }
  63. int index2 = this.getPosition(vertex2);
  64. if(index2==-1) {
  65. System.out.println("顶点"+vertex1+"不存在");
  66. return;
  67. }
  68. //因为是无向图,所以这里直接把两条边都加上
  69. edges[index1][index2]=1;
  70. edges[index2][index1]=1;
  71. }
  72. //获取顶点对应的坐标
  73. public int getPosition(String vertex) {
  74. for (int i = 0; i <size; i++) {
  75. if(vertexs.get(i).equals(vertex)) {
  76. return i;
  77. }
  78. }
  79. //没有则返回-1
  80. return -1;
  81. }
  82. /***************上面已经做好了图,现在做遍历********************/
  83. /**
  84. * 输出节点
  85. * @param index
  86. */
  87. public void display(int index ) {
  88. System.out.println("visit:"+vertexs.get(index));
  89. }
  90. /**
  91. * 打印图
  92. * @param index
  93. */
  94. public void print() {
  95. System.out.print("G ");
  96. for (String vertex: vertexs) {
  97. System.out.print(vertex+" ");
  98. }
  99. System.out.println();
  100. for (int i = 0; i <size; i++) {
  101. String vertex = vertexs.get(i);
  102. System.out.print(vertex+" ");
  103. for (int j = 0; j < size; j++) {
  104. System.out.print(edges[i][j]+" ");
  105. }
  106. System.out.println();
  107. }
  108. System.out.println();
  109. }
  110. /**
  111. * 获取节点index相邻的且没有被访问过的节点下标,没有返回-1
  112. * @param index
  113. * @return
  114. */
  115. public int getAdjacentVertexIndex(int index) {
  116. for (int j = 0; j < size; j++) {
  117. if(edges[index][j]==1&&visited[j]!=1) {
  118. return j;
  119. }
  120. }
  121. return -1;
  122. }
  123. /**
  124. * 深度优先搜索
  125. * @param start 开始的节点
  126. */
  127. public void dfs(String start) {
  128. int index = this.getPosition(start);
  129. //1、初始化一个栈
  130. MyStack stack = new MyStack(size);
  131. //2、顶点入栈
  132. stack.push(index);
  133. //3、标记栈顶为已经访问
  134. this.display(index);
  135. visited[index]=1;
  136. while(!stack.isEmpty()) {
  137. int v= this.getAdjacentVertexIndex(stack.peek());
  138. if(v==-1) {
  139. //如果这个点已经没有邻接顶点了,就弹出
  140. stack.pop();
  141. }else {
  142. //访问到了一个新的节点,直接职标记为已访问
  143. this.display(v);
  144. this.visited[v]=1;
  145. stack.push(v);
  146. }
  147. }
  148. //广度搜索结束后,需要把节点全部变为未搜索,以用于下次搜索
  149. for (int i = 0; i < size; i++) {
  150. visited[i]=0;
  151. }
  152. }
  153. /**
  154. * 广度优先搜索
  155. * @param start 开始的节点
  156. */
  157. public void bfs(String start) {
  158. int index = this.getPosition(start);
  159. //1、初始化一个队列
  160. MyQueue queue = new MyQueue(size);
  161. //2、顶点入队列
  162. queue.enqueue(index);
  163. visited[index]=1;
  164. while(!queue.isEmpty()) {
  165. while(true) {
  166. int v= this.getAdjacentVertexIndex(queue.peek());
  167. if(v==-1) {
  168. //这里表示这个顶点已经没有邻接的了
  169. break;
  170. }else {
  171. //访问到了一个新的节点,直接标记为已经访问
  172. this.visited[v]=1;
  173. queue.enqueue(v);
  174. }
  175. }
  176. //出对列
  177. int front = queue.dequeue();
  178. this.display(front);
  179. }
  180. //深度搜索结束后,需要把节点全部变为未搜索,以用于下次搜索
  181. for (int i = 0; i < size; i++) {
  182. visited[i]=0;
  183. }
  184. }
  185. }
  186. /**
  187. * 深度优先搜索要用到栈,这里自己自定义一个栈,主要操作是
  188. * 出栈:pop
  189. * 入栈:push
  190. * 查看:peek
  191. * 判空:isEmpty
  192. */
  193. class MyStack{
  194. int size;
  195. int[] stack;
  196. int top;//栈顶的位置,出栈只能从栈顶出
  197. //初始化
  198. public MyStack(int size) {
  199. this.stack=new int[size];
  200. this.size=size;
  201. this.top=-1;
  202. }
  203. //入栈
  204. public void push(int j) {
  205. if(top+1==size) {
  206. System.out.println("栈已经满了");
  207. return;
  208. }
  209. stack[++top]=j;
  210. }
  211. //出栈
  212. public int pop() {
  213. if(top==-1) {
  214. return -1;
  215. }
  216. return stack[top--];
  217. }
  218. //查看
  219. public int peek() {
  220. return stack[top];
  221. }
  222. //判断栈空
  223. public boolean isEmpty() {
  224. return (top==-1);
  225. }
  226. }
  227. /**
  228. * 定义队列,广度优先搜索要用到队列,队列基本操作
  229. * 这个是静态队列,一次性的,动态的可以用链表
  230. * 入队:enqueue
  231. * 出队:dequeue
  232. * 查看:peek
  233. * 判空:isEmpty
  234. */
  235. class MyQueue{
  236. int size;
  237. int[] queue;
  238. int front;//队头
  239. int rear;//队尾
  240. public MyQueue(int size) {
  241. this.queue=new int[size];
  242. this.size=size;
  243. this.front=0;
  244. this.rear=-1;
  245. }
  246. //入队:队尾进入
  247. public void enqueue(int j) {
  248. if(rear+1==size) {
  249. System.out.println("队列已满");
  250. return;
  251. }
  252. queue[++rear]=j;
  253. }
  254. //出队:出队从对头出
  255. public int dequeue() {
  256. if (!isEmpty()) {
  257. return queue[front++];
  258. } else {
  259. return -1;
  260. }
  261. }
  262. //查看
  263. public int peek() {
  264. if (!isEmpty()) {
  265. return queue[front];
  266. } else {
  267. return -1;
  268. }
  269. }
  270. //判空
  271. public boolean isEmpty() {
  272. return (rear+1==front);
  273. }
  274. }

运行结果如下:

  1. 输出该图G
  2. G A B C D E
  3. A 0 1 1 1 0
  4. B 1 0 1 0 0
  5. C 1 1 0 1 1
  6. D 1 0 1 0 1
  7. E 0 0 1 1 0
  8. 深度优先搜索从A开始:
  9. visit:A
  10. visit:B
  11. visit:C
  12. visit:D
  13. visit:E
  14. 深度优先搜索从E开始:
  15. visit:E
  16. visit:C
  17. visit:A
  18. visit:B
  19. visit:D
  20. 广度优先搜索从A开始:
  21. visit:A
  22. visit:B
  23. visit:C
  24. visit:D
  25. visit:E
  26. 广度优先搜索从E开始:
  27. visit:E
  28. visit:C
  29. visit:D
  30. visit:A
  31. visit:B

有不足之处,希望大家指正,转载请注明出处。

 783

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2