劍指Offer面試題:5.重建二叉樹


一、題目:重建二叉樹

題目:輸入某二叉樹的前序遍歷和中序遍歷的結果,請重建出該二叉樹。假設輸入的前序遍歷和中序遍歷的結果中都不含重復的數字。例如輸入前序遍歷序列{1,2,4,7,3,5,6,8}和中序遍歷序列{4,7,2,1,5,3,8,6},則重建出如下圖所示的二叉樹並輸出它的頭結點。 

二、解題思路

  在二叉樹的前序遍歷序列中,第一個數字總是樹的根結點的值。但在中序遍歷序列中,根結點的值在序列的中間,左子樹的結點的值位於根結點的值的左邊,而右子樹的結點的值位於根結點的值的右邊。因此我們需要掃描中序遍歷序列,才能找到根結點的值。

  前序遍歷序列的第一個數字1就是根結點的值。掃描中序遍歷序列,就能確定根結點的值的位置。根據中序遍歷特點,在根結點的值1前面的3個數字都是左子樹結點的值,位於1后面的數字都是右子樹結點的值。

  在二叉樹的前序遍歷和中序遍歷的序列中確定根結點的值、左子樹結點的值和右子樹結點的值的步驟如下圖所示:

  分別找到了左、右子樹的前序遍歷序列和中序遍歷序列,我們就可以用同樣的方法分別去構建左右子樹。換句話說,這是一個遞歸的過程。

思路總結:先根據前序遍歷序列的第一個數字創建根結點,接下來在中序遍歷序列中找到根結點的位置,這樣就能確定左、右子樹結點的數量。在前序遍歷和中序遍歷的序列中划分了左、右子樹結點的值之后,就可以遞歸地去分別構建它的左右子樹。

三、解決問題

3.1 代碼實現

    public static Node<int> Construct(int[] preOrder, int[] inOrder, int length)
    {
        // 空指針判斷
        if (preOrder == null || inOrder == null || length <= 0)
        {
            return null;
        }

        return ConstructCore(preOrder, 0, preOrder.Length - 1, inOrder, 0, inOrder.Length - 1);
    }

    public static Node<int> ConstructCore(int[] preOrder, int startPreOrder, int endPreOrder, int[] inOrder, int startInOrder, int endInOrder)
    {
        // 前序遍歷序列的第一個數字是根結點的值
        int rootValue = preOrder[startPreOrder];
        Node<int> root = new Node<int>();
        root.data = rootValue;
        root.lchild = root.rchild = null;

        if (startPreOrder == endPreOrder)
        {
            if (startInOrder == endInOrder && 
                preOrder[startPreOrder] == inOrder[startInOrder])
            {
                return root;
            }
            else
            {
                throw new Exception("Invalid input!");
            }
        }

        // 在中序遍歷中找到根結點的值
        int rootInOrder = startInOrder;
        while (rootInOrder <= endInOrder && inOrder[rootInOrder] != rootValue)
        {
            rootInOrder++;
        }

        // 輸入的兩個序列不匹配的情況
        if (rootInOrder == endInOrder && inOrder[rootInOrder] != rootValue)
        {
            throw new Exception("Invalid input!");
        }

        int leftLength = rootInOrder - startInOrder;
        int leftPreOrderEnd = startPreOrder + leftLength;
        if (leftLength > 0)
        {
            // 構建左子樹
            root.lchild = ConstructCore(preOrder, startPreOrder + 1, leftPreOrderEnd, inOrder, startInOrder, rootInOrder - 1);
        }
        if (leftLength < endPreOrder - startPreOrder)
        {
            // 構建右子樹
            root.rchild = ConstructCore(preOrder, leftPreOrderEnd + 1, endPreOrder, inOrder, rootInOrder + 1, endInOrder);
        }

        return root;
    }

3.2 單元測試

  首先封裝了一個測試主入口,方法定義如下:

    // 單元測試主入口
    public static void ConstructTestPortal(string testName, int[] preOrder, int[] inOrder, int length)
    {
        if (!string.IsNullOrEmpty(testName))
        {
            Console.WriteLine("{0} begins:", testName);
        }
        // 打印先序遍歷
        Console.Write("The preorder sequence is : ");
        for (int i = 0; i < length; i++)
        {
            Console.Write(preOrder[i]);
        }
        Console.Write("\n");
        // 打印中序遍歷
        Console.Write("The inorder sequence is : ");
        for (int i = 0; i < length; i++)
        {
            Console.Write(inOrder[i]);
        }
        Console.Write("\n");

        try
        {
            Node<int> root = Construct(preOrder, inOrder, length);
            BinaryTree<int> bTree = new BinaryTree<int>();
            bTree.Root = root;
            Console.Write("The binary tree is : ");
            // 重建的二叉樹層次遍歷
            bTree.LevelOrder(bTree.Root);
            if (!string.IsNullOrEmpty(testName))
            {
                Console.Write("\n{0} end\n\n", testName);
            }
        }
        catch (Exception)
        {
            Console.WriteLine("Invalid input!");
        }
    }
View Code

  該方法首先接收參數,依次打印先序遍歷和中序遍歷,最后通過調用Construct方法獲得重建的二叉樹的根節點,並實例化一個二叉樹的數據結構(本處的二叉樹結構的實現請閱讀《數據結構基礎溫故-4.樹與二叉樹(上)》),最后輸出重建后的二叉樹的層次遍歷驗證是否重建成功。

  (1)普通二叉樹

    // 普通二叉樹
    //              1
    //           /     \
    //          2       3  
    //         /       / \
    //        4       5   6
    //         \         /
    //          7       8
    public static void ConstructTest1()
    {
        int[] preorder = { 1, 2, 4, 7, 3, 5, 6, 8 };
        int[] inorder = { 4, 7, 2, 1, 5, 3, 8, 6 };

        ConstructTestPortal("ConstructTest1", preorder, inorder, 8);
    }

  (2)所有結點都沒有右子結點

    // 所有結點都沒有右子結點
    //            1
    //           / 
    //          2   
    //         / 
    //        3 
    //       /
    //      4
    //     /
    //    5
    public static void ConstructTest2()
    {
        int[] preorder = { 1, 2, 3, 4, 5 };
        int[] inorder = { 5, 4, 3, 2, 1 };

        ConstructTestPortal("ConstructTest2", preorder, inorder, 5);
    }

  (3)所有結點都沒有左子結點

    // 所有結點都沒有左子結點
    //            1
    //             \ 
    //              2   
    //               \ 
    //                3 
    //                 \
    //                  4
    //                   \
    //                    5
    public static void ConstructTest3()
    {
        int[] preorder = {1, 2, 3, 4, 5};
        int[] inorder = {1, 2, 3, 4, 5};

        ConstructTestPortal("ConstructTest3", preorder, inorder, 5);
    }

  (4)樹中只有一個結點

    // 樹中只有一個結點
    public static void ConstructTest4()
    {
        int[] preorder = { 1 };
        int[] inorder = { 1 };

        ConstructTestPortal("ConstructTest4", preorder, inorder, 1);
    }

  (5)完全二叉樹

    // 完全二叉樹
    //              1
    //           /     \
    //          2       3  
    //         / \     / \
    //        4   5   6   7
    public static void ConstructTest5()
    {
        int[] preorder = {1, 2, 4, 5, 3, 6, 7};
        int[] inorder = {4, 2, 5, 1, 6, 3, 7};

        ConstructTestPortal("ConstructTest5", preorder, inorder, 7);
    }

  (6)輸入空指針

    // 輸入空指針
    public static void ConstructTest6()
    {
        ConstructTestPortal("ConstructTest6", null, null, 0);
    }

  (7)輸入的兩個序列不匹配

    // 輸入的兩個序列不匹配
    public static void ConstructTest7()
    {
        int[] preorder = {1, 2, 4, 5, 3, 6, 7};
        int[] inorder = {4, 2, 8, 1, 6, 3, 7};

        ConstructTestPortal("ConstructTest7", preorder, inorder, 7);
    }

  單元測試的結果如下圖所示:

 


免責聲明!

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



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