1. 程式人生 > >整理五道演算法題系列(1)二叉樹相關

整理五道演算法題系列(1)二叉樹相關

背景

最近想整理一些有意思的題目,特別是給力的破題技巧與思想,均為學習筆記,於此做個長期記錄。不多說,進入演算法世界了~~

說明

=> 五道 / 篇
=> Java
=> 二叉樹相關

題目

1、重建二叉樹

輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重複的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建二叉樹並返回。

思路:首先得了解先序、中序的特點,接著從先序定位根,然後再去中序找出左右子樹,依舊是遞迴實現。

public
TreeNode reConstructBinaryTree(int[] pre, int[] in) { return reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1); } /** * @param pre 前序 * @param startPre 前序首位 * @param endPre 前序末位 * @param in 中序 * @param startIn 中序首位 * @param
endIn 中序末位 * @return */
public TreeNode reConstructBinaryTree(int[] pre, int startPre, int endPre, int[] in, int startIn, int endIn) { // constraint if (startPre > endPre || startIn > endIn) { return null; } // 先序第一位即為根
TreeNode root = new TreeNode(pre[startPre]); for (int i = startIn; i <= endIn; i++) { // 中序找尋根所在位置 if (in[i] == pre[startPre]) { /* 重點解析 1. startPre + i - startIn : 根據中序所找到的根,從而得知下一次遞迴的左子樹,接著找到 前序的左子樹部分,再次根據前序“根左右”的特性傳入startPre 和 endPre,而由於startPre每次都在向 右,因此endPre應該根據startPre來決定 2. (startPre + i - startIn) + 1 :根據中序所找到的根,從而得知下一次遞迴的右子樹,而上一步已 經在前序序列中分隔出根、左子樹、右子樹,那基本可以確定傳入的陣列下標 */ root.left = reConstructBinaryTree(pre, startPre + 1, startPre + i - startIn, in, startIn, i - 1); root.right = reConstructBinaryTree(pre, (startPre + i - startIn) + 1, endPre, in, i + 1, endIn); } } return root; } private class TreeNode { int val; TreeNode left; TreeNode right; public TreeNode(int val) { this.val = val; } }

2、樹的子結構

輸入兩棵二叉樹A,B,判斷B是不是A的子結構。(ps:我們約定空樹不是任意一個樹的子結構)

思路:土方法,一個個比較,但是遞迴比較,以B樹為參考系,從根開始匹配。

public boolean HasSubtree(TreeNode root1, TreeNode root2) {
        boolean result = false;
        if (root2 != null && root1 != null) {
            // 如果找到了對應Tree2的根節點的點
            if (root1.val == root2.val) {
                result = doesTree1HaveTree2(root1, root2);
            }
            // 如果找不到,那麼就再將root的左兒子當作起點,去判斷時包含Tree2
            if (!result) {
                result = doesTree1HaveTree2(root1.left, root2);
            }
            // 如果還找不到,那麼就再將root的右兒子當作起點,去判斷時候包含Tree2
            if (!result) {
                result = HasSubtree(root1.right, root2);
            }
        }
        return result;
    }

    public boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
        /*
        node1 和 node2 的為空判斷務必得記住,一開始挺容易漏掉這個約束的!
         */
        // 如果Tree2已經遍歷完了都能對應得上,返回true
        if (node2 == null) {
            return true;
        }
        // 如果Tree2還沒遍歷完,Tree1卻遍歷完了。返回false
        if (node1 == null) {
            return false;
        }
        // 如果其中有一個點沒有對應上,返回false
        if (node1.val != node2.val) {
            return false;
        }
        // 如果根節點對應的上,那麼就分別去子節點裡面匹配
        return doesTree1HaveTree2(node1.left, node2.left) && doesTree1HaveTree2(node1.right, node2.right);
    }

    private class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }
    }

3、映象二叉樹

操作給定的二叉樹,將其變換為源二叉樹的映象。
輸入描述:

思路:遞迴交換左右節點

public void Mirror(TreeNode root) {
        TreeNode tmp;
        if (root != null) {
            tmp = root.left;
            root.left = root.right;
            root.right = tmp;
            if (root.left != null) {
                Mirror(root.left);
            }
            if (root.right != null) {
                Mirror(root.right);
            }
        }
    }

    private class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }
    }

4、列印二叉樹

從上往下打印出二叉樹的每個節點,同層節點從左至右列印。

思路:先序的遍歷列印方式,巧妙運用Queue實現,利用其“先進先出”的特點。

public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        if (root == null) {
            return list;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        /*
        while迴圈主要針對的是queue是否為空,而queue實際儲存的是每層的根節點,因此便可每層按順序存進list
         */
        while (!queue.isEmpty()) {
            TreeNode treeNode = queue.poll();
            if (treeNode.left != null) {
                queue.offer(treeNode.left);
            }
            if (treeNode.right != null) {
                queue.offer(treeNode.right);
            }
            list.add(treeNode.val);
        }
        return list;
    }

    private class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;

        public TreeNode(int val) {
            this.val = val;

        }
    }

5、二叉搜尋樹的後序遍歷序列

輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則輸出Yes,否則輸出No。假設輸入的陣列的任意兩個數字都互不相同。

思路:首先得了解二叉搜尋樹(BST),接著遞迴判斷是否滿足BST的條件。

public boolean VerifySquenceOfBST(int[] sequence) {
        if (sequence.length == 0) {
            return false;
        }
        if (sequence.length == 1) {
            return true;
        }
        return judge(sequence, 0, sequence.length - 1);
    }

    public boolean judge(int[] a, int start, int end) {
        if (start > end) {
            return true;
        }
        int i = start;
        /*
        找尋左右子樹的分界點
         */
        while (a[i] < a[end]) {
            ++i;
        }
        for (int j = i; j < end; j++) {
            if (a[j] < a[end]) {
                return false;
            }
        }
        /*
        遞迴,分出左右子樹再次遞迴判斷
         */
        return judge(a, start, i - 1) && judge(a, i, end - 1);
    }