二叉树之恢复

先来一段不痛不痒的文:

先复习一下二叉树的先、中、后序遍历的访问顺序(在节点存在的情况下):

1、先序遍历(VLR) 根节点-> 左孩子-> 右孩子

2、中序遍历(LVR) 左孩子-> 根节点-> 右孩子

3、后序遍历(LRV) 左孩子-> 右孩子-> 根节点

假设现在有

一组先序遍历输出的字符序列const char *VLR = "ABCDEFGH";

一组中序遍历输出的字符序列const char *LVR = "CBEDFAGH";

一组后序遍历输出的字符序列const char *LRV = "CEFDBHGA";

根据三种遍历的输出特点,我们可以肯定,VLR中的第一个字符A一定是一个根节点,且是整颗二叉树的根节点,再根据中序序列先左再根后右的特点可以知道,在LVRA左边的序列CBEDF,这5个字符都属于A的左子树,A右边的2个字符构成的序列GH一定属于A的右子树,将对创建左子树有用的序列CBEDF称创建左子树时的作有效序列,对创建右子树的序列GH也称作创建右子树时的有效序列

再来看VLR中的第二个输出B,但从VLR中看,我们不能断言B就是A的左孩子,因为存在A的左孩子不存在且BA的右孩子的可能,这种可能下,BA之后紧挨着A输出,但是我们可以结合LVR来看(VLR不能结合LRV来看,按照和之前同样的推法你可以发现这两种序列下的节点关系并不唯一)VLR中的第二个输出B,在LVR序列中位于A的左边,所以B应该是A的左子树,目前有关系如下:

 

在看VLR中第三个C,在LVR中位于A的左边B的左边,所以CBA的共同的左子树,但二叉树中节点的最大度为2,所以得出关系如下:

 

第三个D,在LVR中位于A的左边,CB的右边,可得出关系如下:

 

第四个E,在LVR中位于A的左边,CB的右边,但是在D的左边,可以得出关系如下:

 

其余的输出也是按照同样的方法去处理,LVRLRV也是一样的道理




//LinkBinTreeRestore.h

#ifndef _LINKBINTREERESTORE_H_
#define _LINKBINTREERESTORE_H_
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

typedef	char Element;

typedef struct _BinTreeNode
{
    Element	data;
    struct _BinTreeNode *leftChild;
    struct _BinTreeNode *rightChild;
}BinTreeNode;


typedef BinTreeNode *BinTree;

void InitBinTree(BinTree *tree);

//根据前、后序遍历结果无法唯一确定二叉树,故只有前中序、中后序创建
void CreateBinTree_1(BinTree *tree, const char *VLR, const char *LVR, int n);
void CreateBinTree_2(BinTree *tree, const char *LVR, const char *LRV, int n);


bool BinTreeCmp(BinTree tree1, BinTree tree2);
void DestroyBinTree(BinTree *tree);

#endif //_LINKBINTREERESTORE_H_



//LinkBinTreeRestore.c

#include "LinkBinTreeRestore.h"

void InitBinTree(BinTree *tree)
{
    *tree = NULL;
}

/*
 * 以先序遍历输出的字符作为每个节点数据域的填值依据,以中序遍历输出的字符间的位置信息作为每个节点创建的位置依据
 *
 *参数VLR是先序遍历输出结果字符串,LVR是中序遍历输出字符串,n为该次创建字符串的有效长度,如在创建A的左子树时,LVR中A的左边的字符序列CBEDF有效,n值为5
 */
void CreateBinTree_1(BinTree *tree, const char *VLR, const char *LVR, int n)
{
    if( 0 == n )
    {
        *tree = NULL;
        return ;
    }

    int i = 0;
    while (VLR[0] != LVR[i])                        //寻找先序遍历的输出在中序遍历中的位置,以分割左右子树
        i++;
    *tree = (BinTreeNode *)malloc(sizeof(BinTreeNode));     //创建根节点
    if( NULL == *tree )
        return ;
    (*tree)->data = VLR[0];

    /*以创建根节点A的左右子树为例(有效序列也就是LVR中根的左边或右边的序列):
     *创建左子树时,以VLR序列中的第二个输出B(开始位置VLR + 1)开始的序列创建A的左子树,该序列的BCDEF属于有效部分,对于创建左子树LVR序列
     * 的CBEDF部分(开始位置LVR)都是有效的,i用于控制有效部分长度,控制前两个参数有效部分的长度,创建A的左子树时为5
     */
    CreateBinTree_1(&(*tree)->leftChild, VLR + 1, LVR, i);                      //创建左子树
    //创建右子树时,VLR序列中的有效序列为GH为有效部分(开始位置VLR + i + 1),LVR序列有效部分GH(开始位置LVR + i + 1),创建A的右子树时有效长为2
    CreateBinTree_1(&(*tree)->rightChild, VLR + i + 1, LVR + i + 1, n - i - 1); //创建右子树
}

void CreateBinTree_2(BinTree *tree, const char *LVR, const char *LRV, int n)
{
    if( 0 == n )
    {
        *tree = NULL;
        return;
    }

    int i = 0;
    while (LRV[n - 1] != LVR[i])
        i++;
    *tree = (BinTreeNode *)malloc(sizeof(BinTreeNode));
    if( NULL == *tree )
        return ;
    (*tree)->data = LRV[n - 1];

    /*以创建根节点A的左右子树为例(有效序列也就是LVR中根的左边或右边的序列):
     * 创建左子树时,LVR序列的CBEDF为有效序列(起始位置LVR),LRV中CEFDB为有效序列(起始位置LRV),有效长度i在创建A的左子树时为5
     */
    CreateBinTree_2(&(*tree)->leftChild, LVR, LRV, i);
    /*/创建右子树时,LVR序列的有效部分为GH(起始位置为LVR + i + 1),LRV中有效序列为HG(起始位置LRV + i),有效长度在创建A的右子树时为2
     */
    CreateBinTree_2(&(*tree)->rightChild, LVR + i + 1, LRV + i, n - i - 1);

}

bool BinTreeCmp(BinTreeNode *node1, BinTreeNode *node2)
{
    if( NULL == node1 && NULL == node2 )                                //同为空则相等
        return true;
    if( NULL != node1 && NULL != node2 && node1->data == node2->data )  //都不为空且节点数据域值相等,判断其子树是否相等
        return BinTreeCmp(node1->leftChild, node2->leftChild) && BinTreeCmp(node1->rightChild, node2->rightChild);

    return false;           //其余情况不等
}

void DestroyBinTree(BinTree *tree)
{
    if( NULL == *tree )
        return;
    DestroyBinTree(&(*tree)->leftChild);
    DestroyBinTree(&(*tree)->rightChild);
    free(*tree);
    *tree = NULL;
}





//main.c

#include <stdio.h>
#include <string.h>
#include "LinkBinTreeRestore.h"

int main(int argc, char *argv[])
{
    const char *VLR = "ABCDEFGH";
    const char *LVR = "CBEDFAGH";
    const char *LRV = "CEFDBHGA";
    BinTree tree1;
    BinTree tree2;
    InitBinTree(&tree1);
    InitBinTree(&tree2);

    CreateBinTree_1(&tree1, VLR, LVR, strlen(VLR));
    CreateBinTree_2(&tree2, LVR, LRV, strlen(LVR));

    BinTreeCmp(tree1, tree2) ? printf("树相同\n") : printf("树不同\n");

    DestroyBinTree(&tree1);
    DestroyBinTree(&tree2);

    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 输入一棵二叉树的前序遍历和中序遍历,输出该二叉树的后序遍历。 解题思路: 根据二叉树的前序遍历和中序遍历可以确定一棵二叉树。前序遍历的第一个节点是根节点,根据中序遍历可以得到左子树和右子树的节点个数,从而可以将前序遍历和中序遍历分成左子树和右子树的两部分,递归构建左子树和右子树。 递归终止条件为前序遍历和中序遍历为空,此时返回空节点。 C++代码: ```cpp #include <iostream> #include <vector> #include <unordered_map> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { unordered_map<int, int> map; for (int i = 0; i < inorder.size(); i++) { map[inorder[i]] = i; } return buildTree(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, map); } private: TreeNode* buildTree(vector<int>& preorder, int preStart, int preEnd, vector<int>& inorder, int inStart, int inEnd, unordered_map<int, int>& map) { if (preStart > preEnd || inStart > inEnd) { return NULL; } int rootVal = preorder[preStart]; int rootIndex = map[rootVal]; int leftNodes = rootIndex - inStart; TreeNode* root = new TreeNode(rootVal); root->left = buildTree(preorder, preStart + 1, preStart + leftNodes, inorder, inStart, rootIndex - 1, map); root->right = buildTree(preorder, preStart + leftNodes + 1, preEnd, inorder, rootIndex + 1, inEnd, map); return root; } }; void postorder(TreeNode* root) { if (root == NULL) { return; } postorder(root->left); postorder(root->right); cout << root->val << " "; } int main() { vector<int> preorder = {1, 2, 4, 5, 3, 6}; vector<int> inorder = {4, 2, 5, 1, 3, 6}; Solution solution; TreeNode* root = solution.buildTree(preorder, inorder); postorder(root); return 0; } ``` 输出结果为: ``` 4 5 2 6 3 1 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值