1. 程式人生 > >Java資料結構和演算法(二)樹的基本操作

Java資料結構和演算法(二)樹的基本操作

Java資料結構和演算法(二)樹的基本操作

一、樹的遍歷

二叉樹遍歷分為:前序遍歷、中序遍歷、後序遍歷。即父結點的訪問順序

1.1 前序遍歷

前序遍歷

基本思想:先訪問根結點,再先序遍歷左子樹,最後再先序遍歷右子樹即根—左—右。圖中前序遍歷結果是:1,2,4,5,7,8,3,6。

// 遞迴實現前序遍歷
public void preOrder() {
    System.out.printf("%s ", value);
    if (left != null) {
        left.preOrder1();
    }
    if (right != null) {
        right.preOrder1();
    }
}

// 非遞迴實現前序遍歷
public void preOrder1() {
    TreeNode<E> head = this;
    Stack<TreeNode<E>> stack = new Stack();
    stack.push(head);
    while (!stack.isEmpty()) {
        TreeNode<E> pop = stack.pop();
        System.out.printf("%s ", head.value);
        if (pop.right != null) {
            stack.push(pop.right);
        }
        if (pop.left != null) {
            stack.push(pop.left);
        }
    }
}

1.2 中序遍歷

中序遍歷

// 遞迴實現中序遍歷
public void midOrder() {
    if (left != null) {
        left.preOrder1();
    }
    System.out.printf("%s ", value);
    if (right != null) {
        right.preOrder1();
    }
}

// 非遞迴實現中序遍歷
public void midOrder1() {
    TreeNode<E> head = this;
    Stack<TreeNode<E>> stack = new Stack();
    while (!stack.isEmpty() || head != null) {
        if (head != null) {
            // 先將左結點全部入棧
            stack.push(head);
            head = head.left;
        } else {
            // 左結點全部入棧後就需要依次彈出,並處理右結點
            head = stack.pop();
            System.out.printf("%s ", head.value);
            head = head.right;
        }
    }
}

1.3 後序遍歷

後序遍歷

// 遞迴實現後序遍歷
public void postOrder() {
    if (left != null) {
        left.preOrder1();
    }
    if (right != null) {
        right.preOrder1();
    }
    System.out.printf("%s ", value);
}

// 非遞迴實現後序遍歷
public void postOrder2() {
    TreeNode<E> head = this;
    Stack<TreeNode<E>> stack1 = new Stack();
    Stack<TreeNode<E>> stack2 = new Stack();
    stack1.push(head);
    while (!stack1.isEmpty()) {
        TreeNode<E> tmp = stack1.pop();
        stack2.push(tmp);
        if (tmp.left != null) {
            stack1.push(tmp.left);
        }
        if (tmp.right != null) {
            stack1.push(tmp.right);
        }
    }
    while (!stack2.isEmpty()) {
        TreeNode<E> tmp = stack2.pop();
        System.out.printf("%s ", tmp.value);
    }
}

1.4 層次遍歷

public void levelOrder() {
    TreeNode<E> head = this;
    Queue<TreeNode<E>> queue = new ArrayDeque<>();
    queue.offer(head);
    while (!queue.isEmpty()) {
        for (int i = 0; i < queue.size(); i++) {
            TreeNode<E> tmp = queue.poll();
            System.out.printf(String.valueOf(tmp.value) + " ");
            if (tmp.left != null) {
                queue.offer(tmp.left);
            }
            if (tmp.right != null) {
                queue.offer(tmp.right);
            }
        }
    }
}

二、樹的深度

// 非遞迴求樹的最大和最小深度
public int maxLevel() {
    int level = 0;
    TreeNode<E> head = this;
    Queue<TreeNode<E>> queue = new ArrayDeque<>();
    queue.offer(head);
    while (!queue.isEmpty()) {
        for (int i = 0; i < queue.size(); i++) {
            level++;
            TreeNode<E> tmp = queue.poll();
            if (tmp.left != null) {
                queue.offer(tmp.left);
            }
            if (tmp.right != null) {
                queue.offer(tmp.right);
            }
        }
    }
    return level;
}

public int minLevel() {
    int level = 0;
    TreeNode<E> head = this;
    Queue<TreeNode<E>> queue = new ArrayDeque<>();
    queue.offer(head);
    while (!queue.isEmpty()) {
        for (int i = 0; i < queue.size(); i++) {
            level++;
            TreeNode<E> tmp = queue.poll();
            if (tmp.left == null && tmp.right == null) {
                return level;
            }
            if (tmp.left != null) {
                queue.offer(tmp.left);
            }
            if (tmp.right != null) {
                queue.offer(tmp.right);
            }
        }
    }
    return 0;
}

// 遞迴求樹的最大和最小深度
public int minLevel(TreeNode head) {
    if (head == null) {
        return 0;
    }
    if (head.left == null && head.right == null) {
        return 1;
    }
    if (head.left == null && head.right != null) {
        return minLevel(head.left) + 1;
    }
    if (head.left != null && head.right == null) {
        return minLevel(head.right) + 1;
    }
    return Math.min(minLevel(head.left), minLevel(head.right)) + 1;
}

三、求兩個節點的公共祖先

// 遞迴求兩個結點的公共祖先,一個結點可以是自己的祖先
public TreeNode ancestor(TreeNode root, TreeNode node1, TreeNode node2) {
    if (root == node1 || root == node2) {
        return root;
    }
    TreeNode left = ancestor(root.left, node1, node2);
    TreeNode right = ancestor(root.right, node1, node2);
    if (left == null || right == null) {
        return root;
    }
    return left != null ? left : right;
}

每天用心記錄一點點。內容也許不重要,但習慣很重要!