1. 程式人生 > >java中樹的遍歷,包括先序遍歷、中序遍歷、後序遍歷以及廣度優先遍歷跟深度優先遍歷

java中樹的遍歷,包括先序遍歷、中序遍歷、後序遍歷以及廣度優先遍歷跟深度優先遍歷

先總結一下各種遍歷方式的區別

前序遍歷:根結點 ---> 左子樹 ---> 右子樹

中序遍歷:左子樹---> 根結點 ---> 右子樹

後序遍歷:左子樹 ---> 右子樹 ---> 根結點

廣度優先,從左到右

深度優先,從上至下

程式碼:


package com.ietree.basic.datastructure.tree.binarytree;

import java.util.*;

public class ThreeLinkBinTree<E> {

    public static class TreeNode {

        Object data;
        TreeNode left;
        TreeNode right;
        TreeNode parent;

        public TreeNode() {

        }

        public TreeNode(Object data) {
            this.data = data;
        }

        public TreeNode(Object data, TreeNode left, TreeNode right, TreeNode parent) {
            this.data = data;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }

    }

    private TreeNode root;

    /**
     * 以預設的構造器建立二叉樹
     */
    public ThreeLinkBinTree() {
        this.root = new TreeNode();
    }

    /**
     * 以指定根元素建立二叉樹
     */
    public ThreeLinkBinTree(E data) {
        this.root = new TreeNode(data);
    }

    /**
     * 為指定節點新增子節點
     *
     * @param parent 需要新增子節點的父節點的索引
     * @param data   新子節點的資料
     * @param isLeft 是否為左節點
     * @return 新增的節點
     */
    public TreeNode addNode(TreeNode parent, E data, boolean isLeft) {

        if (parent == null) {
            throw new RuntimeException(parent + "節點為null, 無法新增子節點");
        }
        if (isLeft && parent.left != null) {
            throw new RuntimeException(parent + "節點已有左子節點,無法新增左子節點");
        }
        if (!isLeft && parent.right != null) {
            throw new RuntimeException(parent + "節點已有右子節點,無法新增右子節點");
        }

        TreeNode newNode = new TreeNode(data);
        if (isLeft) {
            // 讓父節點的left引用指向新節點
            parent.left = newNode;
        } else {
            // 讓父節點的left引用指向新節點
            parent.right = newNode;
        }
        // 讓新節點的parent引用到parent節點
        newNode.parent = parent;

        return newNode;
    }

    /**
     * 判斷二叉樹是否為空
     */
    public boolean empty() {
        // 根據元素判斷二叉樹是否為空
        return root.data == null;
    }

    // 返回根節點
    public TreeNode root() {
        if (empty()) {
            throw new RuntimeException("樹為空,無法訪問根節點");
        }
        return root;
    }

    // 返回指定節點(非根節點)的父節點
    public E parent(TreeNode node) {
        if (node == null) {
            throw new RuntimeException("節點為null,無法訪問其父節點");
        }
        return (E) node.parent.data;
    }

    // 返回指定節點(非葉子)的左子節點,當左子節點不存在時返回null
    public E leftChild(TreeNode parent) {
        if (parent == null) {
            throw new RuntimeException(parent + "節點為null,無法新增子節點");
        }
        return parent.left == null ? null : (E) parent.left.data;
    }

    // 返回指定節點(非葉子)的右子節點,當右子節點不存在時返回null
    public E rightChild(TreeNode parent) {
        if (parent == null) {
            throw new RuntimeException(parent + "節點為null,無法新增子節點");
        }
        return parent.right == null ? null : (E) parent.right.data;
    }

    // 返回該二叉樹的深度
    public int deep() {
        // 獲取該樹的深度
        return deep(root);
    }

    // 這是一個遞迴方法:每一棵子樹的深度為其所有子樹的最大深度 + 1
    private int deep(TreeNode node) {
        if (node == null) {
            return 0;
        }
        // 沒有子樹
        if (node.left == null && node.right == null) {
            return 1;
        } else {
            int leftDeep = deep(node.left);
            int rightDeep = deep(node.right);
            // 記錄其所有左、右子樹中較大的深度
            int max = leftDeep > rightDeep ? leftDeep : rightDeep;
            // 返回其左右子樹中較大的深度 + 1
            return max + 1;
        }
    }

    // 實現先序遍歷
    // 1、訪問根節點
    // 2、遞迴遍歷左子樹
    // 3、遞迴遍歷右子樹
    public List<TreeNode> preIterator() {
        return preIterator(root);
    }

    private List<TreeNode> preIterator(TreeNode node) {

        List<TreeNode> list = new ArrayList<TreeNode>();
        // 處理根節點
        list.add(node);

        // 遞迴處理左子樹
        if (node.left != null) {
            list.addAll(preIterator(node.left));
        }

        // 遞迴處理右子樹
        if (node.right != null) {
            list.addAll(preIterator(node.right));
        }

        return list;

    }

    // 實現中序遍歷
    // 1、遞迴遍歷左子樹
    // 2、訪問根節點
    // 3、遞迴遍歷右子樹
    public List<TreeNode> inIterator() {
        return inIterator(root);
    }

    private List<TreeNode> inIterator(TreeNode node) {

        List<TreeNode> list = new ArrayList<TreeNode>();

        // 遞迴處理左子樹
        if (node.left != null) {
            list.addAll(inIterator(node.left));
        }

        // 處理根節點
        list.add(node);

        // 遞迴處理右子樹
        if (node.right != null) {
            list.addAll(inIterator(node.right));
        }

        return list;

    }

    // 實現後序遍歷
    // 1、遞迴遍歷左子樹
    // 2、遞迴遍歷右子樹
    // 3、訪問根節點
    public List<TreeNode> postIterator() {
        return postIterator(root);
    }

    private List<TreeNode> postIterator(TreeNode node) {

        List<TreeNode> list = new ArrayList<TreeNode>();

        // 遞迴處理左子樹
        if (node.left != null) {
            list.addAll(postIterator(node.left));
        }

        // 遞迴處理右子樹
        if (node.right != null) {
            list.addAll(postIterator(node.right));
        }

        // 處理根節點
        list.add(node);

        return list;

    }

    /**
     * 實現廣度優先遍歷
     * 廣度優先遍歷又稱為按層遍歷,整個遍歷演算法先遍歷二叉樹的第一層(根節點),再遍歷根節點的兩個子節點(第二層),以此類推
     */
    public List<TreeNode> bFS() {

        Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
        List<TreeNode> list = new ArrayList<TreeNode>();
        if (root != null) {
            // 將根元素加入“佇列”
            queue.offer(root);
        }
        while (!queue.isEmpty()) {
            // 將該佇列的“隊尾”的元素新增到List中
            list.add(queue.peek());
            TreeNode p = queue.poll();
            // 如果左子節點不為null,將它加入“佇列”
            if (p.left != null) {
                queue.offer(p.left);
            }
            // 如果右子節點不為null,將它加入“佇列”
            if (p.right != null) {
                queue.offer(p.right);
            }
        }
        return list;
    }

    /**
     * 深度遍歷
     *
     * @return
     */
    public ArrayList<TreeNode> dFS() {
        ArrayList<TreeNode> lists = new ArrayList<TreeNode>();
        Stack<TreeNode> stack = new Stack();
        if (root == null) {
            return lists;
        }
        stack.push(root);
        while (!stack.isEmpty()) {
            TreeNode tree = stack.pop();
            //先往棧中壓入右節點,再壓左節點,這樣出棧就是先左節點後右節點了。
            if (tree.right != null) {
                stack.push(tree.right);
            }
            if (tree.left != null) {
                stack.push(tree.left);
            }
            lists.add(tree);
        }
        return lists;
    }

}