• 剑指offer笔记面试题7----重建二叉树


    题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如,输入前序遍历序列{1, 2, 4, 7, 3, 5, 6, 8}和中序遍历序列{4, 7, 2, 1, 5 3, 8, 6},则重建如下图所示的二叉树并输出它的头结点。二叉树的节点的定义如下:

    struct BinaryTreeNode{
    	int m_nValue;
    	BinaryTreeNode* m_pLeft;
    	BinaryTreeNode* m_pRight;
    };
    //              1
    //           /     
    //          2       3  
    //         /       / 
    //        4       5   6
    //                  /
    //          7       8
    

    测试用例:

    • 普通二叉树(完全二叉树,不完全二叉树)。
    • 特殊二叉树(所有节点都没有右子节点的二叉树;所有节点都没有左子节点的二叉树;只有一个节点的二叉树)。
    • 特殊的输入测试(二叉树的根节点指针为nullptr;输入的前序遍历序列和中序遍历序列不匹配)。

    测试代码:

    void Test(char* testName, int* preorder, int* inorder, int length)
    {
        if(testName != nullptr)
            printf("%s begins:
    ", testName);
        printf("The preorder sequence is: ");
        for(int i = 0; i < length; ++ i)
            printf("%d ", preorder[i]);
        printf("
    ");
        printf("The inorder sequence is: ");
        for(int i = 0; i < length; ++ i)
            printf("%d ", inorder[i]);
        printf("
    ");
        try
        {
            BinaryTreeNode* root = Construct(preorder, inorder, length);
            PrintTree(root);
            DestroyTree(root);
        }
        catch(std::exception& exception)
        {
            printf("Invalid Input.
    ");
        }
    }
    
    // 普通二叉树
    //              1
    //           /     
    //          2       3  
    //         /       / 
    //        4       5   6
    //                  /
    //          7       8
    void Test1()
    {
        const int length = 8;
        int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
        int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};
        Test("Test1", preorder, inorder, length);
    }
    
    // 所有结点都没有右子结点
    //            1
    //           / 
    //          2   
    //         / 
    //        3 
    //       /
    //      4
    //     /
    //    5
    void Test2()
    {
        const int length = 5;
        int preorder[length] = {1, 2, 3, 4, 5};
        int inorder[length] = {5, 4, 3, 2, 1};
        Test("Test2", preorder, inorder, length);
    }
    
    // 所有结点都没有左子结点
    //            1
    //              
    //              2   
    //                
    //                3 
    //                 
    //                  4
    //                   
    //                    5
    void Test3()
    {
        const int length = 5;
        int preorder[length] = {1, 2, 3, 4, 5};
        int inorder[length] = {1, 2, 3, 4, 5};
        Test("Test3", preorder, inorder, length);
    }
    
    // 树中只有一个结点
    void Test4()
    {
        const int length = 1;
        int preorder[length] = {1};
        int inorder[length] = {1};
        Test("Test4", preorder, inorder, length);
    }
    
    // 完全二叉树
    //              1
    //           /     
    //          2       3  
    //         /      / 
    //        4   5   6   7
    void Test5()
    {
        const int length = 7;
        int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
        int inorder[length] = {4, 2, 5, 1, 6, 3, 7};
        Test("Test5", preorder, inorder, length);
    }
    
    // 输入空指针
    void Test6()
    {
        Test("Test6", nullptr, nullptr, 0);
    }
    
    // 输入的两个序列不匹配
    void Test7()
    {
        const int length = 7;
        int preorder[length] = {1, 2, 4, 5, 3, 6, 7};
        int inorder[length] = {4, 2, 8, 1, 6, 3, 7};
        Test("Test7: for unmatched input", preorder, inorder, length);
    }
    

    本题考点:

    • 考查应聘者对二叉树的前序遍历和中序遍历的理解程度。只有对二叉树的不同遍历算法有了深刻的理解,应聘者才有可能在遍历序列中划分出左、右子树对应的子序列。
    • 考查应聘者分析复杂问题的能力。我们把构建二叉树的大问题分解成构建左、右子树的两个小问题。我们发现小问题和大问题在本质上是一致的,因此可以用递归的方式解决。

    实现代码:

    /*********************************BinaryTree.h************************************/
    struct BinaryTreeNode 
    {
        int                    m_nValue; 
        BinaryTreeNode*        m_pLeft;  
        BinaryTreeNode*        m_pRight; 
    };
    
    BinaryTreeNode* CreateBinaryTreeNode(int value);
    void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight);
    void PrintTreeNode(const BinaryTreeNode* pNode);
    void PrintTree(const BinaryTreeNode* pRoot);
    void DestroyTree(BinaryTreeNode* pRoot);
    
    /*********************************BinaryTree.cpp************************************/
    #include <cstdio>
    #include "BinaryTree.h"
    
    BinaryTreeNode* CreateBinaryTreeNode(int value)
    {
        BinaryTreeNode* pNode = new BinaryTreeNode();
        pNode->m_nValue = value;
        pNode->m_pLeft = nullptr;
        pNode->m_pRight = nullptr;
        return pNode;
    }
    
    void ConnectTreeNodes(BinaryTreeNode* pParent, BinaryTreeNode* pLeft, BinaryTreeNode* pRight)
    {
        if(pParent != nullptr)
        {
            pParent->m_pLeft = pLeft;
            pParent->m_pRight = pRight;
        }
    }
    
    void PrintTreeNode(const BinaryTreeNode* pNode)
    {
        if(pNode != nullptr)
        {
            printf("value of this node is: %d
    ", pNode->m_nValue);
    
            if(pNode->m_pLeft != nullptr)
                printf("value of its left child is: %d.
    ", pNode->m_pLeft->m_nValue);
            else
                printf("left child is nullptr.
    ");
    
            if(pNode->m_pRight != nullptr)
                printf("value of its right child is: %d.
    ", pNode->m_pRight->m_nValue);
            else
                printf("right child is nullptr.
    ");
        }
        else
        {
            printf("this node is nullptr.
    ");
        }
        printf("
    ");
    }
    
    void PrintTree(const BinaryTreeNode* pRoot)
    {
        PrintTreeNode(pRoot);
    
        if(pRoot != nullptr)
        {
            if(pRoot->m_pLeft != nullptr)
                PrintTree(pRoot->m_pLeft);
            if(pRoot->m_pRight != nullptr)
                PrintTree(pRoot->m_pRight);
        }
    }
    
    void DestroyTree(BinaryTreeNode* pRoot)
    {
        if(pRoot != nullptr)
        {
            BinaryTreeNode* pLeft = pRoot->m_pLeft;
            BinaryTreeNode* pRight = pRoot->m_pRight;
            delete pRoot;
            pRoot = nullptr;
            DestroyTree(pLeft);
            DestroyTree(pRight);
        }
    }
    
    /*********************************ConstructBinaryTree.cpp************************************/
    #include "..UtilitiesBinaryTree.h"
    #include <exception>
    #include <cstdio>
    
    BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder);
    
    BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
    {
        if(preorder == nullptr || inorder == nullptr || length <= 0)
            return nullptr;
        return ConstructCore(preorder, preorder + length - 1,
            inorder, inorder + length - 1);
    }
    
    BinaryTreeNode* ConstructCore
    (
        int* startPreorder, int* endPreorder, 
        int* startInorder, int* endInorder
    )
    {
        // 前序遍历序列的第一个数字是根结点的值
        int rootValue = startPreorder[0];
        BinaryTreeNode* root = new BinaryTreeNode();
        root->m_nValue = rootValue;
        root->m_pLeft = root->m_pRight = nullptr;
    
        if(startPreorder == endPreorder)
        {
            if(startInorder == endInorder && *startPreorder == *startInorder)
                return root;
            else
                throw std::exception("Invalid input.");
        }
    
        // 在中序遍历中找到根结点的值
        int* rootInorder = startInorder;
        while(rootInorder <= endInorder && *rootInorder != rootValue)
            ++ rootInorder;
        if(rootInorder == endInorder && *rootInorder != rootValue)
            throw std::exception("Invalid input.");
        int leftLength = rootInorder - startInorder;
        int* leftPreorderEnd = startPreorder + leftLength;
        if(leftLength > 0)
        {
            // 构建左子树
            root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd, 
                startInorder, rootInorder - 1);
        }
        if(leftLength < endPreorder - startPreorder)
        {
            // 构建右子树
            root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
                rootInorder + 1, endInorder);
        }
        return root;
    }
    int main(int argc, char* argv[])
    {
        Test1();
        Test2();
        Test3();
        Test4();
        Test5();
        Test6();
        Test7();
        int a;
        scanf("%d", &a);
        return 0;
    }
    
  • 相关阅读:
    Singleton patterns 单件(创建型模式)
    JS的运算问题……
    Java 新手学习日记一
    pycharm远程调试配置
    MATLAB2010安装方法
    人生三境界
    SAS数据步与过程步,数据步语句
    Google Chrome浏览器调试功能介绍
    认识Java标识符
    java多态和继承
  • 原文地址:https://www.cnblogs.com/tangliang39/p/11694105.html
Copyright © 2020-2023  润新知