二叉樹 - DFS與BFS


二叉樹 - DFS與BFS

​ 深度優先遍歷 (DFS Depth First Search) 就是一個節點不到頭(葉子節點為空) 不回頭

​ 廣度有點遍歷(BFS Breadth First Search) 就是一層一層輸出 , 輸出到最下層的葉子節點, 為空的時候結束

​ 其中深度遍歷就是我們所說的 先序遍歷 中序遍歷 后序遍歷 , 先中后指的是根節點輸出的時機,先就是根左右

數據結構如下, 全文都是

public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int x) {
        val = x;
    }

輸出要求

List<Integer> search(TreeNode root) {
   // do 需要返回一個數組
}

測試數據

TreeNode root = new TreeNode(1);
TreeNode left = new TreeNode(2);
TreeNode right = new TreeNode(3);
root.left = left;
root.right = right;
left.left = new TreeNode(4);
left.right = new TreeNode(5);
right.left = new TreeNode(6);

1. DFS

1. 遞歸實現

​ 遞歸實現代碼相當之簡單 , 所以很容易寫, 就算不會也能記憶下來

1. 先序遍歷

private List<Integer> preOrder(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    recursion(list, root);
    return list;
}

// 遞歸
private void recursion(List<Integer> list, TreeNode root) {
    if (null == root) return;
    // 根
    list.add(root.val);
    // 左
    recursion(list, root.left);
    // 右
    recursion(list, root.right);
}

2. 中序遍歷

private List<Integer> midOrder(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    recursion(list, root);
    return list;
}


private void recursion(List<Integer> list, TreeNode root) {
    if (null == root) return;
    recursion(list, root.left);
    // 調換到中間
    list.add(root.val);
    recursion(list, root.right);
}

3. 后序遍歷

private List<Integer> aftOrder(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    recursion(list, root);
    return list;
}


private void recursion(List<Integer> list, TreeNode root) {
    if (null == root) return;
    recursion(list, root.left);
    recursion(list, root.right);
    // 調換到最后
    list.add(root.val);
}

2. 遞歸執行流程

三種流程基本都差不多

​ 好多人對於遞歸並不了解, 執行流程 , 我們知道方法的出棧需要一個return, 所以遞歸就是在找這個 , 就拿我們上面說的那個先序遍歷為例子吧 .

3. 非遞歸實現(很重要)

​ 遞歸的壞處就是 , 出入棧消耗大量的內存, 每一次方法的調用都會保存大量的變量, 多以對於遍歷來說並不好 ,

​ 非遞歸遍歷的實現 , 基於棧的實現, 對於遍歷節點保存在棧中, 出入棧 , 主要利用棧的后進先出的特性 , 很好的保證了, 后進的優先遍歷 .

1. 先序遍歷

非遞歸實現先序遍歷

private List<Integer> preOrderUnRecursion(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    // 棧
    LinkedList<TreeNode> stack = new LinkedList<TreeNode>();

    // 壓棧
    stack.push(root);
    while (stack.size() > 0) {
        // 出棧
        TreeNode node = stack.pop();

        TreeNode right = node.right;
        if (null != right) {
            stack.push(right);
        }
        
        TreeNode left = node.left;
        
        if (null != left) {
            stack.push(left);
        }

        list.add(node.val);
    }
    return list;
}

2. 中序遍歷

​ 這個實現就比較麻煩了 , 因為先序遍歷, 根節點有先天的優勢可以先出去 ,所以很

private List<Integer> midOrderUnRecursion(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    // 棧
    LinkedList<TreeNode> stack = new LinkedList<TreeNode>();

    // 壓棧
    stack.push(root);

    while (stack.size() > 0) {
        // 出棧
        TreeNode node = stack.pop();

        TreeNode right = node.right;
        TreeNode left = node.left;

        if (null != right) {
            node.right = null;
            stack.push(right);
        }
		
        // 重復入棧 , 是因為根節點不是最先出來的
        if (null != right || null != left) {
            stack.push(node);
        }

        if (null != left) {
            node.left = null;
            stack.push(left);
        }

        if (null == left && null == right) {
            list.add(node.val);
        }
    }
    return list;
}

3. 后序遍歷

private List<Integer> aftFirstSearchUnRecursion(TreeNode root) {
    if (null == root) return Collections.emptyList();
    List<Integer> list = new ArrayList<>();
    // 棧
    LinkedList<TreeNode> stack = new LinkedList<TreeNode>();

    // 壓棧
    stack.push(root);

    while (stack.size() > 0) {
        // 出棧
        TreeNode node = stack.pop();

        TreeNode right = node.right;
        TreeNode left = node.left;

        if (null != right || null != left) {
            stack.push(node);
        }

        if (null != right) {
            node.right = null;
            stack.push(right);
        }

        if (null != left) {
            node.left = null;
            stack.push(left);
        }

        if (null == left && null == right) {
            list.add(node.val);
        }
    }
    return list;
}

4.非遞歸實現流程圖

1. 先序遍歷

2. 中序遍歷和后序遍歷一樣

流程從左往右, 從上往下看 .

2. BFS

​ 廣度優先遍歷就是一層 一層遍歷 , 同一層, 從左到右輸出,

​ 基於隊列實現的 , FIFO特性 , offer 和 poll , 操作

代碼實現

private List<Integer> breadthFirstSearch(TreeNode root) {
    if (null == root) return Collections.emptyList();
    ArrayList<Integer> list = new ArrayList<>();
    LinkedList<TreeNode> queue = new LinkedList<>();
    
    queue.offer(root);
    
    while (queue.peek() != null) {

        TreeNode poll = queue.poll();

        TreeNode left = poll.left;
        if (null != left) {
            queue.offer(left);
        }

        TreeNode right = poll.right;
        if (null != right) {
            queue.offer(right);
        }

        list.add(poll.val);
}

基本流程圖

3. 求樹的深度

​ 利用樹的先序遍歷遞歸進行求樹的深度

private int countDepth(TreeNode root) {
    if (null == root) return 0;
    int left = countDepth(root.left);
    int right = countDepth(root.right);
    return left >= right ? left + 1 : right + 1;
}

4. 求數的節點個數

​ 也是遞歸遍歷

private int countNode(TreeNode root) {
    if (null == root) return 0;
    return countNode(root.left) + countNode(root.right)+1;
}


免責聲明!

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



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