java二叉樹遍歷——深度優先(DFS)與廣度優先(BFS) 遞歸版與非遞歸版


介紹

深度優先遍歷:從根節點出發,沿着左子樹方向進行縱向遍歷,直到找到葉子節點為止。然后回溯到前一個節點,進行右子樹節點的遍歷,直到遍歷完所有可達節點為止。

廣度優先遍歷:從根節點出發,在橫向遍歷二叉樹層段節點的基礎上縱向遍歷二叉樹的層次。

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)

廣度優先搜索算法是從根節點開始,沿着樹的寬度遍歷樹的節點。如果所有節點均被訪問,則算法中止。

遞歸實現


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);
                }
            }
        }
    }
}
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM