数据结构-深度优先搜索Java实现

目录

一、引言

深度优先搜索(DFS)是一种在图或树中进行搜索的算法,它沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所有边都已探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

DFS是一种盲目搜索,即它并不需要知道目标的位置,只是尽可能地遍历所有可能的路径。因此,它的搜索效率并不高,特别是在面对大规模数据时。然而,DFS可以找到目标值或解决目标问题,对于解决NP问题作用很大。

此外,DFS如同数据结构中的栈结构,是一种后进先出的结构,导致了所有的点进入栈时有一个顺序,我们称之为 "DFS序"。

二、算法步骤

深度优先搜索算法的步骤如下:

  1. 选取图中某一顶点Vi为出发点,访问并标记该顶点。
  2. 以Vi为当前顶点,依次搜索Vi的每个邻接点Vj,若Vj未被访问过,则访问和标记邻接点Vj,若Vj已被访问过,则搜索Vi的下一个邻接点。
  3. 以Vj为当前顶点,重复步骤2),直到图中和Vi有路径相通的顶点都被访问为止。
  4. 若图中尚有顶点未被访问过(非连通的情况下),则可任取图中的一个未被访问的顶点作为出发点,重复上述过程,直至图中所有顶点都被访问。

三、原理演示

递归实现

  1. 选择起始节点:从图中的一个起始节点开始,将该节点标记为已访问。
  2. 递归探索邻居节点:对于当前节点,遍历它的邻居节点。如果邻居节点尚未被访问,就递归地调用深度优先搜索函数,以此邻居节点为新的起始节点,重复步骤1。
  3. 回溯:当当前节点的所有邻居节点都被访问后,回溯到上一个节点,并继续遍历其未被访问的邻居节点。
  4. 重复步骤2和3,直到图中的所有节点都被访问。

下面是递归实现深度优先搜索的示例代码:

java 复制代码
public void dfsRecursive(Node node, Set<Node> visited) {
   visited.add(node);
   System.out.print(node.value + " ");
   
   for (Node neighbor : node.neighbors) {
       if (!visited.contains(neighbor)) {
           dfsRecursive(neighbor, visited);
       }
   }
}

非递归实现(使用堆栈)

  1. 选择起始节点:从图中的一个起始节点开始,将该节点标记为已访问,并将它入栈。
  2. 迭代遍历:使用一个循环,不断从栈中弹出节点,然后遍历它的邻居节点。
  3. 探索邻居节点:对于当前弹出的节点,遍历其邻居节点。如果邻居节点尚未被访问,就将其标记为已访问并将其入栈。
  4. 重复步骤2和3,直到栈为空。

下面是非递归实现深度优先搜索的示例代码:

java 复制代码
public void dfsIterative(Node start) {
   Stack<Node> stack = new Stack<>();
   Set<Node> visited = new HashSet<>();

   stack.push(start);
   visited.add(start);

   while (!stack.isEmpty()) {
       Node current = stack.pop();
       System.out.print(current.value + " ");
       
      for (Node neighbor : current.neighbors) {
          if (!visited.contains(neighbor)) {
               stack.push(neighbor);
               visited.add(neighbor);
           }
       }
   }
}

无论采用递归还是非递归方式,深度优先搜索的关键思想是深入到尽可能深的层级,直到无法再深入为止,然后回溯到上一个节点,继续探索。这一过程不断重复,直到遍历整个图。深度优先搜索对于解决路径查找、拓扑排序、连通性检测等问题都非常有用。

四、代码实战

以下是深度优先搜索算法的Java代码实现:

java 复制代码
import java.util.*;

public class DFS {
   private int V; // 顶点数量
   private LinkedList<Integer> adj[]; // 邻接表

   // 构造函数
   DFS(int v) {
       V = v;
       adj = new LinkedList[v];
       for (int i = 0; i < v; ++i) {
           adj[i] = new LinkedList();
       }
   }

   // 添加边
   void addEdge(int v, int w) {
       adj[v].add(w);
   }

   // DFS函数
   void DFSUtil(int v, boolean visited[]) {
       // 标记当前节点为已访问并输出该节点
       visited[v] = true;
       System.out.print(v + " ");

       // 递归访问与v节点相邻的未访问节点
       Iterator<Integer> i = adj[v].listIterator();
       while (i.hasNext()) {
           int n = i.next();
           if (!visited[n])
               DFSUtil(n, visited);
       }
   }

   // DFS函数
   void DFS(int v) {
       // 标记所有顶点为未访问状态
      boolean visited[] = new boolean[V];

       // 调用递归函数DFSUtil开始从顶点v进行DFS遍历
       DFSUtil(v, visited);
   }
}

五、结论

我们一起来总结一下:

深度优先搜索在计算机科学中有着广泛的应用,例如用于遍历树或图的结构、查找路径、解决优化问题等。它的优点在于空间复杂度相对较小,可以处理大规模的数据,同时可以避免搜索冗余的节点。然而,深度优先搜索也有其局限性,例如可能会陷入死循环或无法找到最优解,因此需要根据具体问题选择合适的算法进行优化。

点赞收藏,富婆包养✋✋

相关推荐
跟着珅聪学java33 分钟前
spring boot +Elment UI 上传文件教程
java·spring boot·后端·ui·elementui·vue
我命由我1234538 分钟前
Spring Boot 自定义日志打印(日志级别、logback-spring.xml 文件、自定义日志打印解读)
java·开发语言·jvm·spring boot·spring·java-ee·logback
lilye6639 分钟前
程序化广告行业(55/89):DMP与DSP对接及数据统计原理剖析
java·服务器·前端
徐小黑ACG2 小时前
GO语言 使用protobuf
开发语言·后端·golang·protobuf
想跑步的小弱鸡4 小时前
Leetcode hot 100(day 3)
算法·leetcode·职场和发展
战族狼魂4 小时前
CSGO 皮肤交易平台后端 (Spring Boot) 代码结构与示例
java·spring boot·后端
xyliiiiiL5 小时前
ZGC初步了解
java·jvm·算法
杉之6 小时前
常见前端GET请求以及对应的Spring后端接收接口写法
java·前端·后端·spring·vue
爱的叹息6 小时前
RedisTemplate 的 6 个可配置序列化器属性对比
算法·哈希算法
hycccccch6 小时前
Canal+RabbitMQ实现MySQL数据增量同步
java·数据库·后端·rabbitmq