深度优先(DFS)与广度优先搜索(BFS)递归版与非递归版

Posted 张可

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度优先(DFS)与广度优先搜索(BFS)递归版与非递归版相关的知识,希望对你有一定的参考价值。

介绍

最近系统学习算法时发现网上关于这两种算法的文章参差不齐,不太统一,自己也花了点时间总结了一下,现在在这里分享出来。


树的实现

树是一种特殊的有向图,实现方式有很多,我这里用最常见也是最简单的方式来实现:

public class TreeNode<V> {
private V value;
private List<TreeNode<V>> childList;//子节点列表
    public TreeNode(V value) {        this.value = value; }
    public TreeNode(V value, List<TreeNode<V>> childList) {        this.value = value;        this.childList = childList; }
    public V getValue() {        return value; }
    public void setValue(V value) {        this.value = value; }
    public List<TreeNode<V>> getChildList() {        return childList; }
    public void setChildList(List<TreeNode<V>> childList) {        this.childList = childList; }
}

下面基于这种方式实现的树来编写这两种遍历算法。


树的两种遍历算法

关于这两种算法的介绍分析网上实在是太多了,我就不详细说了,直接放代码。


深度优先搜索算法(DFS)

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


递归实现

public static <V> void dfs(TreeNode<V> tree, int depth) {    if (tree != null) {        //打印节点值以及深度        System.out.println(tree.getValue().toString() + ",   " + depth);        if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {            for (TreeNode<V> item : tree.getChildList()) {                dfs(item, depth + 1);            }        }    }}


非递归实现

public static <V> void dfsNotRecursive(TreeNode<V> tree) {    if (tree != null) {        //次数之所以用 Map 只是为了保存节点的深度,        //如果没有这个需求可以改为 Stack<TreeNode<V>>        Stack<Map<TreeNode<V>, Integer>> stack = new Stack<>();        Map<TreeNode<V>, Integer> root = new HashMap<>();        root.put(tree, 0);        stack.push(root);        while (!stack.isEmpty()) {            Map<TreeNode<V>, Integer> item = stack.pop();            TreeNode<V> node = item.keySet().iterator().next();            int depth = item.get(node);            //打印节点值以及深度            System.out.println(tree.getValue().toString() + ",   " + depth);            if (node.getChildList() != null && !node.getChildList().isEmpty()) {                for (TreeNode<V> treeNode : node.getChildList()) {                    Map<TreeNode<V>, Integer> map = new HashMap<>();                    map.put(treeNode, depth + 1);                    stack.push(map);                }            }        }    }}


分类

一般来说 DFS 算法又分为如下三种:


1. 前序遍历(Pre-Order Traversal) :指先访问根,然后访问子树的遍历方式

private static <V> void dfs(TreeNode<V> tree, int depth) {    if (d != null) {        //打印节点值以及深度        System.out.println(tree.getValue().toString() + ",   " + depth);        if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {            for (TreeNode<V> item : tree.getChildList()) {                dfs(item, depth + 1);            }        }    }}



2. 后序遍历(Post-Order Traversal):指先访问子树,然后访问根的遍历方式

private static <V> void dfs(TreeNode<V> tree, int depth) {    if (d != null) {        if (tree.getChildList() != null && !tree.getChildList().isEmpty()) {            for (TreeNode<V> item : tree.getChildList()) {                dfs(item, depth + 1);            }        }        //打印节点值以及深度        System.out.println(tree.getValue().toString() + ",   " + depth);    }}


3. 中序遍历(In-Order Traversal):指先访问左(右)子树,然后访问根,最后访问右(左)子树的遍历方式。

中序遍历一般是用二叉树实现:

private static <V> void dfs(TreeNode<V> root, int depth) {    if (root.getLeft() != null){        dfs(root.getLeft(), depth + 1); }
    if (root.getRight() != null){        dfs(root.getRight(), depth + 1); }
    //打印节点值以及深度    System.out.println(d.getValue().toString() + ",   " + depth);}


广度优先搜索算法(Breadth-First Search,BFS)

BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。


递归实现

public static <V> void bfs(List<TreeNode<V>> children, int depth) {    List<TreeNode<V>> thisChildren, allChildren = new ArrayList<>();    for (TreeNode<V> child: children) {        //打印节点值以及深度        System.out.println(child.getValue().toString() + ",   " + depth);        thisChildren = child.getChildList();        if (thisChildren != null && thisChildren.size() > 0) {            allChildren.addAll(thisChildren);        }    }    if (allChildren.size() > 0)  {        bfs(allChildren, depth + 1);    }}


递归实现的方式我自己想了好久没想出来,最后还是在网上搜到的算法。

可以看到非递归实现有个问题就是无法遍历根节点,不过问题不大,而且我也还没想出来其他更优雅的办法来实现。


非递归实现

public static <V> void bfsNotRecursive(TreeNode<V> tree) {    if (tree != null) {        //跟上面一样,使用 Map 也只是为了保存树的深度,没这个需要可以不用 Map        Queue<Map<TreeNode<V>, Integer>> queue = new ArrayDeque<>();        Map<TreeNode<V>, Integer> root = new HashMap<>();        root.put(tree, 0);        queue.offer(root);        while (!queue.isEmpty()) {            Map<TreeNode<V>, Integer> itemMap = queue.poll();            TreeNode<V> itemTreeNode = itemMap.keySet().iterator().next();            int depth = itemMap.get(itemTreeNode);            //打印节点值以及深度            System.out.println(itemTreeNode.getValue().toString() + ",   " + depth);            if (itemTreeNode.getChildList() != null &&                    !itemTreeNode.getChildList().isEmpty()) {                for (TreeNode<V> child : itemTreeNode.getChildList()) {                    Map<TreeNode<V>, Integer> map = new HashMap<>();                    map.put(child, depth + 1);                    queue.offer(map);                }            }        }    }}

相比较而言,非递归实现就比较简单了。

以上是关于深度优先(DFS)与广度优先搜索(BFS)递归版与非递归版的主要内容,如果未能解决你的问题,请参考以下文章

深度优先搜索(DFS)与广度优先搜索(BFS)

数据结构与算法图遍历算法 ( 深度优先搜索 DFS | 深度优先搜索和广度优先搜索 | 深度优先搜索基本思想 | 深度优先搜索算法步骤 | 深度优先搜索理论示例 )

Python算法-深度优先搜索&广度优先搜索(DFS&BFS)

什么时候使用深度优先搜索 (DFS) 与广度优先搜索 (BFS) 比较实用? [关闭]

基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)

深度优先搜索(DFS)与广度优先搜索(BFS)的Java实现