Java深度优先搜索DFS(含面试大厂题和源码)

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

深度优先搜索的实现方式:

  1. 递归实现:使用递归函数实现DFS,每次递归调用都访问一个未被访问的相邻节点。
  2. 非递归实现(栈):使用栈来模拟递归过程,每次从栈中取出一个节点,访问其未被访问的相邻节点,并将其压入栈中。

深度优先搜索的应用:

  • 解决迷宫问题:DFS 可以用来找到迷宫的解,通过不断尝试不同的路径直到找到出口。
  • 拓扑排序:在有向无环图(DAG)中,DFS 可以用来进行拓扑排序。
  • 寻找连通分量:在无向图中,DFS 可以用来找到连通分量。
  • 路径查找:在图论中,DFS 可以用来寻找两个节点之间的路径。

深度优先搜索的Java实现(栈):

import java.util.*;

public class DepthFirstSearch {
    private Map<Integer, List<Integer>> graph;
    private boolean[] visited;

    public DepthFirstSearch(Map<Integer, List<Integer>> graph) {
        this.graph = graph;
        this.visited = new boolean[graph.size()];
    }

    public void dfs(int start) {
        Stack<Integer> stack = new Stack<>();
        stack.push(start);
        visited[start] = true;

        while (!stack.isEmpty()) {
            int node = stack.pop();
            System.out.print(node + " ");

            for (int neighbor : graph.get(node)) {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    stack.push(neighbor);
                }
            }
        }
    }

    public static void main(String[] args) {
        Map<Integer, List<Integer>> graph = new HashMap<>();
        graph.put(0, Arrays.asList(1, 2));
        graph.put(1, Arrays.asList(3));
        graph.put(2, Arrays.asList(3));
        graph.put(3, Collections.emptyList());
        graph.put(4, Arrays.asList(5));
        graph.put(5, Collections.emptyList());

        DepthFirstSearch dfs = new DepthFirstSearch(graph);
        dfs.dfs(0); // 应输出 0 1 3 2
    }
}

在面试中,深度优先搜索是一个重要的算法问题,它考察应聘者对图的遍历和递归的理解。通过实现深度优先搜索,可以展示你对基本算法和数据结构的掌握程度。希望这些知识点和示例代码能够帮助你更好地准备面试!

题目 1:二叉树的深度

描述
给定一个二叉树,找出其最大深度。

示例

输入: 二叉树
        3
       / \
      9  20
        /   \
       15   7
输出: 3

Java 源码

public class MaxDepth {
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }

    public static void main(String[] args) {
        MaxDepth solution = new MaxDepth();
        TreeNode root = new TreeNode(3);
        root.left = new TreeNode(9);
        root.right = new TreeNode(20);
        root.left.right = new TreeNode(15);
        root.right.left = new TreeNode(7);
        int result = solution.maxDepth(root);
        System.out.println("Max depth of binary tree: " + result);
    }
}

class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(int x) { val = x; }
}

题目 2:路径总和

描述
给定一个二叉树和一个目标和,确定所有从根到叶子的路径上总和等于目标和的路径。

示例

输入: 二叉树,sum = 25
        5
       / \
      4   8
     /   / \
    11  13  4
     /  \      \
   7    2      9
输出: [[5,4,11,7], [5,8,4,4]]

Java 源码

public class PathSum {
    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        List<List<Integer>> result = new ArrayList<>();
        List<Integer> path = new ArrayList<>();
        dfs(root, sum, path, result);
        return result;
    }

    private void dfs(TreeNode node, int remaining, List<Integer> path, List<List<Integer>> result) {
        if (node == null) {
            return;
        }
        path.add(node.val);
        if (node.left == null && node.right == null && remaining == node.val) {
            result.add(new ArrayList<>(path));
        }
        dfs(node.left, remaining - node.val, path, result);
        dfs(node.right, remaining - node.val, path, result);
        path.remove(path.size() - 1);
    }

    public static void main(String[] args) {
        PathSum solution = new PathSum();
        TreeNode root = new TreeNode(5);
        root.left = new TreeNode(4);
        root.right = new TreeNode(8);
        root.left.left = new TreeNode(11);
        root.left.right = new TreeNode(13);
        root.right.left = new TreeNode(4);
        root.right.right = new TreeNode(7);
        root.left.right.left = new TreeNode(2);
        root.left.right.right = new TreeNode(9);
        List<List<Integer>> result = solution.pathSum(root, 25);
        System.out.println("Path sum: " + result);
    }
}

题目 3:子图同构

描述
给定两个有向图,判断它们是否是同构的。如果一个图的节点可以一一映射到另一个图的节点,并且映射后的图与原图结构相同,则这两个图是同构的。

示例

输入: 两个图的邻接表表示
图1: [[1,2],[2,3],[3,4]]
图2: [[1,2],[2,3],[3,4]]
输出: true

Java 源码

public class GraphIsomorphism {
    public boolean isIsomorphic(Map<Integer, List<Integer>> graph1, Map<Integer, List<Integer>> graph2) {
        if (graph1.size() != graph2.size()) {
            return false;
        }
        Map<Integer, Integer> mapping = new HashMap<>();
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(1);
        mapping.put(1, 1);
        while (!queue.isEmpty()) {
            int node1 = queue.poll();
            int node2 = mapping.get(node1);
            List<Integer> neighbors1 = graph1.get(node1);
            List<Integer> neighbors2 = graph2.get(node2);
            if (!neighbors1.size() == neighbors2.size()) {
                return false;
            }
            for (int i = 0; i < neighbors1.size(); i++) {
                int neighbor1 = neighbors1.get(i);
                int neighbor2 = neighbors2.get(i);
                if (!mapping.containsKey(neighbor1)) {
                    queue.offer(neighbor1);
                    mapping.put(neighbor1, neighbor2);
                } else if (!mapping.get(neighbor1).equals(neighbor2)) {
                    return false;
                }
            }
        }
        return true;
    }

    public static void main(String[] args) {
        GraphIsomorphism solution = new GraphIsomorphism();
        Map<Integer, List<Integer>> graph1 = new HashMap<>();
        graph1.put(1, Arrays.asList(2));
        graph1.put(2, Arrays.asList(3));
        graph1.put(3, Arrays.asList(4));
        Map<Integer, List<Integer>> graph2 = new HashMap<>();
        graph2.put(1, Arrays.asList(2));
        graph2.put(2, Arrays.asList(3));
        graph2.put(3, Arrays.asList(4));
        boolean result = solution.isIsomorphic(graph1, graph2);
        System.out.println("Graphs are isomorphic: " + result);
    }
}

这些题目和源码展示了深度优先搜索在解决实际问题中的应用。在面试中,能够根据问题的特点选择合适的算法并实现其解决方案是非常重要的。希望这些示例能够帮助你更好地准备面试!

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
/* * (有向)图的深度优先遍历算法模板 */ package dsa; public abstract class DFS extends GraphTraverse { //变量 protected static int clock = 0;//遍历过程中使用的计时钟 //构造方法 public DFS(Graph g) { super(g); } //深度优先遍历算法 protected Object traverse(Vertex v, Object info) {//从顶点v出发,做深度优先查找 if (UNDISCOVERED != v.getStatus()) return null;//跳过已访问过的顶点(针对非连通图) v.setDStamp(clock++); v.setStatus(DISCOVERED); visit(v, info);//访问当前顶点 for (Iterator it = v.outEdges(); it.hasNext();) {//检查与顶点v Edge e = (Edge)it.getNext();//通过边e = (v, u) Vertex u = (Vertex)e.getVPosInV(1).getElem();//相联的每一顶点u switch (u.getStatus()) {//根据u当前的不同状态,分别做相应处理 case UNDISCOVERED ://若u尚未被发现,则 e.setType(TREE);//e被归类为“树边” traverse(u, info);//从u出发,继续做深度优先查找 break; case DISCOVERED ://若u已经被发现,但对其访问尚未结束,则 e.setType(BACKWARD);//将e归类为“后向跨边” break; default ://VISITED,即对u的访问已经结束 if (u.getDStamp() < v.getDStamp())//若相对于v,u被发现得更早,则 e.setType(CROSS);//将e归类为“横跨边” else//否则 e.setType(FORWARD);//将e归类为“前向跨边” break; } }//至此,v的所有邻居都已访问结束,故 v.setFStamp(clock++); v.setStatus(VISITED);//将v标记为VISITED return null;//然后回溯 } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值