[关闭]
@HUST-SuWB 2018-04-22T07:58:40.000000Z 字数 3203 阅读 486

深搜&广搜

Finlabtech


定义

深度优先搜索算法(Depth-First-Search,缩写为 DFS),是一种利用递归实现的搜索算法。简单来说,其搜索过程和 “不撞南墙不回头” 类似[1]
广度优先搜索算法(Breadth-First-Search,缩写为 BFS),是一种利用队列实现的搜索算法。简单来说,其搜索过程和 “湖面丢进一块石头激起层层涟漪” 类似[2]

应用

这两种算法用途都非常广泛,比如走迷宫是一种很常见的搜索算法的应用。网上介绍这两种算法的文章也非常多,这里我也不多废话,而且我表述的也不一定准确。这里给两个例子吧,在看《啊哈算法》的时候书中抛出的两个问题,正好是通过BFS&DFS解决的。

打印1-9的全排列

  1. public class DFS {
  2. //标记已经使用了的数字
  3. private static int[] book;
  4. //符合条件的排列数
  5. private static int[] a;
  6. public static void main(String[] args) {
  7. Scanner sc = new Scanner(System.in);
  8. int N = sc.nextInt();
  9. book = new int[N+1];
  10. a = new int[N+1];
  11. //从第一位开始
  12. dfs(1, N);
  13. }
  14. private static void dfs(int n, int N) {
  15. if(n==N+1) {
  16. for(int i=1; i<N+1; i++) {
  17. System.out.print(a[i]);
  18. }
  19. System.out.println("");
  20. return;
  21. }
  22. for(int i=1; i<N+1; i++) {
  23. if(book[i] == 0) {
  24. a[n] = i;
  25. book[i] = 1;
  26. dfs(n+1, N);
  27. //这一句太重要了!但是很难理解啊。
  28. book[i] = 0;
  29. }
  30. }
  31. }
  32. }

走迷宫

  1. public class BFS {
  2. //记录已经被扫描到的节点
  3. private Queue<Node> queue = new LinkedList<>();
  4. private List<Node> already = new ArrayList<>();
  5. public static void main(String[] args) {
  6. Scanner sc = new Scanner(System.in);
  7. int xLength = sc.nextInt();
  8. int yLength = sc.nextInt();
  9. //0表示通,1表示不通
  10. int[][] maze = new int[xLength][yLength];
  11. for(int i=0;i<xLength;i++) {
  12. for(int j=0;j<yLength;j++) {
  13. maze[i][j] = sc.nextInt();
  14. }
  15. }
  16. Node startNode = new Node(sc.nextInt(), sc.nextInt());
  17. Node targetNode = new Node(sc.nextInt(), sc.nextInt());
  18. BFS bfs = new BFS();
  19. bfs.bfs(maze, startNode, targetNode);
  20. }
  21. private void bfs(int[][] maze, Node startNode, Node targetNode) {
  22. List<Node> neighbors = getNeighbor(startNode, maze);
  23. for(Node node : neighbors) {
  24. queue.offer(node);
  25. }
  26. while(!queue.isEmpty()) {
  27. Node next = queue.poll();
  28. if(already.contains(next)) {
  29. System.out.println("已走过!");
  30. } else {
  31. already.add(next);
  32. if(next.getX()==targetNode.getX() && next.getY()==targetNode.getY()) {
  33. System.out.println("走到了(" + next.getX() + ", " + next.getY() + ")这个点,结束");
  34. break;
  35. } else {
  36. System.out.println("走到了(" + next.getX() + ", " + next.getY() + ")这个点");
  37. neighbors = getNeighbor(next, maze);
  38. for(Node node : neighbors) {
  39. queue.offer(node);
  40. }
  41. }
  42. }
  43. }
  44. }
  45. /**
  46. * 或者每个节点的临近节点
  47. * @param a
  48. * @param maze
  49. * @return
  50. */
  51. private List<Node> getNeighbor(Node a, int[][] maze) {
  52. List<Node> neighbors = new ArrayList<>();
  53. int[][] path = new int[][]{{1,0}, {0,1}, {-1,0}, {0, -1}};
  54. for(int i=0; i<path.length; i++) {
  55. Node next = march(a, path[i]);
  56. if(next.getX() >=0 && next.getX() < maze.length && next.getY() >=0 && next.getY() < maze.length) {
  57. if(maze[next.getX()][next.getY()] == 0) {
  58. neighbors.add(next);
  59. }
  60. }
  61. }
  62. return neighbors;
  63. }
  64. /**
  65. * 定义上下左右的走法
  66. * @param source
  67. * @param path
  68. * @return
  69. */
  70. private Node march(Node source, int[] path) {
  71. return new Node(source.getX()+path[0], source.getY()+path[1]);
  72. }
  73. }
  74. class Node {
  75. int x;
  76. int y;
  77. Node(int x, int y) {
  78. this.x = x;
  79. this.y = y;
  80. }
  81. int getX() {
  82. return x;
  83. }
  84. int getY() {
  85. return y;
  86. }
  87. void setY(int y) {
  88. this.y = y;
  89. }
  90. @Override
  91. public boolean equals(Object o) {
  92. if (o == this) return true;
  93. if (!(o instanceof Node)) {
  94. return false;
  95. }
  96. Node node = (Node) o;
  97. if(node.getX() == x && node.getY() == y) {
  98. return true;
  99. } else {
  100. return false;
  101. }
  102. }
  103. }

总结

最后,截取我看到的一段总结放在最后,慢慢理解[3]

  1. 搜索与DP,贪心,分治的共同点在于状态的转移,只不过在搜索中,一个阶段的最优状态是由之前所有状态得到的,这是其余其他算法的区别之处。
  2. 状态的转移即由某一个不同的状态转移至另一个不同的状态,DFS在于从一个初始状态出发,一直转移状态直到搜到目标或搜到状态无法进行转移为止,其中的剪枝便是通过应题目具体要求和仔细分析而去掉某些没必要或不存在的状态以节约时间和空间,而BFS在于一层一层地扩展状态,这样首先找到的目标便一定是用时或步数最少的。DFS几乎适用于所有情况,因为这本身是一种遍历所有状态的搜索,只不过在寻找最小步或最小时间的情况下比较慢,无法快速找到目标,但在程序世界里DFS本身也有不适用的情况,如果状态没有下限即转移的深度没有下限那么便无法深搜,同样,BFS也可能存在一层状态都扩展不完的情况,这样便才会有迭代加深搜索等扩展搜索的出现,它结合了两者的优点且实际应用效果不错。紫书上面的名词“解答树”便很好地描述了所有状态转移的过程,当然这个似乎与我们无关,但是其实也是一种刻画搜索过程的有利工具。
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注