重建二叉树6

本文介绍如何根据给定的前序遍历和中序遍历结果重建二叉树。利用递归方法找到根节点,并划分左右子树,最终完成整棵树的构建。文章还提供了层次遍历的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

树的逻辑很简单:1.除了根节点之外,每个节点都有一个父节点,根节点没有父节点(也可为NULL);2.除了叶节点之外,每个节点都有一个或多个子节点,叶节点没有子节点。3.父节点和子节点之间用指针链接。
树的遍历方式:
1. 前序遍历 VLR
2. 中序遍历 LVR
3. 后序遍历 LRV
在此以重建二叉树为例,了解二叉树的前、中序遍历。
题目描述:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出下图所示的二叉树并输出它的头节点。

二叉树的节点定义如下:

struct BinTreeNode{
  int m_value;
  BinTreeNode *left;
  BinTreeNode *right;  
};

解题思路:由于输入的是先序遍历和中序遍历,通过先序遍历我们可以知道根节点为先序遍历的第一个数字,知道根节点后就可以在中序遍历中查找到此节点,并以此节点确定其左侧为左子树,右侧为右子树。最后递归的构建其左右子树。
测试用例:

//层次遍历,借助队列
void levels(BinTreeNode *root){
    if(root == NULL)
        return;
    std::queue<BinTreeNode*> Q;
    Q.push(root);
    while(!Q.empty()){//如果队列不为空
        //赋值x,避免造成死循环
        BinTreeNode *x = Q.front();
        //先访问根节点(队首)的值
        std::cout << x->m_value << " ";
        //访问后删除队首节点
        Q.pop();
        //如果有左孩子
        if(x->left)
            Q.push(x->left);//入队
        //如果有右孩子
        if(x->right)
            Q.push(x->right);//入队
    }
}

//测试用例
int main(){
    //先序遍历序列
    int preOrder[8] = {1, 2, 4, 7, 3, 5, 6, 8};
    //中序遍历序列
    int inOrder[8] = {4, 7, 2, 1, 5, 3, 8, 6};
    //重建后返回的结果
    BinTreeNode *root = new BinTreeNode;
    root = NULL;

    root = constructBinTree(preOrder, inOrder, 8);
    //打印重建后的二叉树,层次遍历
    levels(root);//Output:1, 2, 3, 4, 5, 6, 7, 8

    return 0;
}

constructBinTree函数实现

//函数参数依次为:先序数组首地址, 先序数组尾地址, 中序数组首地址, 中序数组尾地址
BinTreeNode* constructCore(int pre[], int endPre[], int in[], int endIn[]){
    //先序遍历第一个节点必然是根结点
    int rootValue = pre[0];
    //这时可以创建二叉树树根
    BinTreeNode* root = new BinTreeNode;
    root->m_value = rootValue;
    root->left = NULL;
    root->right = NULL;

    //判段是否只有一个根节点值
    if(pre == endPre){
        if(in == endIn && *pre == *in)
            return root;
        else//先序和中序值不一致
            throw new std::exception("Invalid input.");
    }

    //在中序遍历中查找根节点
    int *rootInOrder = in; //因为会执行递增操作,所以要创建一个副本,在副本进行
    while(rootInOrder != endIn && *rootInOrder != rootValue){//直到遇到根节点值停止
        ++rootInOrder;
    }
    //遍历后,作此判断。若中序遍历中没有找到这个根值,则表示输入非法
    if(rootInOrder == endIn && *rootInOrder != rootValue)
        throw new std::exception("Invalid input.");

    //若至此,在中序遍历找到根节点后,其左侧是左子树,右侧即右子树
    //求出左侧长度
    int leftLength = rootInOrder - in;
    //在先序遍历中,找到左子树节点结束的位置 = 先序开始 + 左侧长度(中序求出的)
    int *leftPreOrderEnd = pre + leftLength;

    //递归的构建二叉树的左右子树
    if(leftLength > 0)//构建左子树传入值:先序开始位置,先序左子树结束位置,中序开始位置,中序左子树结束位置-1
        root->left = constructCore(pre+1, leftPreOrderEnd, in, rootInOrder-1);
    if(leftLength < endPre - pre)//构建右子树传入值:先序右子树开始位置, 先序结束位置,中序右子树开始位置
        root->right = constructCore(leftPreOrderEnd+1, endPre, rootInOrder+1, endIn);
    return root;
}

BinTreeNode* constructBinTree(int pre[], int in[], int length){
    //判断是否为空及长度是否小于等于0
    if(pre == NULL || in == NULL || length <= 0)
        return NULL;
    //使用辅助函数, 核心构造代码
    return constructCore(pre, pre + length-1, in, in + length-1);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值