已知先序、中序求后序;已知中序、后序求先序(C++)

先序中序后序的相互求解问题

一、问题描述

我们知道,二叉树有三种深度优先遍历方法:先序中序以及后序,那么,如何已知其中两种遍历序列,求解第三种遍历序列呢。

其实也没大家想得那么美好哈,已知先序、中序可以确定后序,已知中序,后序可以确定先序,但是已知先序和后序是不能确定中序的。究其原因,其实很简单,因为中序可以确定根节点的左孩子是哪块,右孩子是哪块,就是可以确定父子关系嘛。但是如果知道先序和后序的话,举一个例子,看下面的图:


先序为:1 2;后序为:2 1

先序为:1 2;后序为 2 1

所以,其实已知先序和后序时,是不能确定二叉树的,但是如果含有中序序列,那是可以的。

二、解决方案

我们看下如果手算是怎样的、、举个例子哈,先从先序中序开始:

先序:1 2 3 4 5 6

中序:3 2 4 1 6 5

(1)首先,从先序来看,我们知道1是先序的第一个元素,也是这棵二叉树的根节点。

(2)接着,我们找中序里面对应的1的位置,找到了之后,我们发现3 2 4是在1的左边,也就是说这些都是1的左孩子,先不管这些内部复杂的亲子兄弟关系了。6 5是在1的右边,也就是说这些都是1的右孩子。

(3)在先序中找2 3 4的第一个,是2,在中序里面对应的2左边是3,右边是4,分别是它的左右孩子。

(4)在先序中找6 5,发现第一个是5,在中序里面对应的5地左边是6,右边没有,也就是说,5只有一个左孩子,是6。

(5)由此得到的树为:

已知中序和后序的也很类似,就不详述了。

那么,下面介绍用算法实现的步骤是什么,其实就是简单的递归:

(1)确定根,确定左子树,确定右子树。

(2)在左子树中递归。

(3)在右子树中递归。

(4)打印当前根。

这样看上去似乎很简单,下面看代码好了

三、代码实现

(1)已知先序中序求后序

<pre name="code" class="cpp">struct TreeNode{//每一个节点的属性:左节点、右节点、数据
    int data;
    TreeNode* left;
    TreeNode* right;
};
void BinaryTreeFromOrderings(int *inorder,int *preorder,int length)//中序序列,先序序列,中序中需要寻找的字段长度
{
    if(length==0)
        return;

    TreeNode* node=new TreeNode;//新建一个节点,节点的数据为先序的首个元素
    node->data=*preorder;

    int rootIndex;//根节点在中序数组中的下标
    for(rootIndex=0;rootIndex<length;rootIndex++)
        if(inorder[rootIndex]==*preorder)
            break;

    //后序遍历输出结果
    BinaryTreeFromOrderings(inorder,preorder+1,rootIndex);//中序的左边一半长度作为新的需要寻找的字段长度
    BinaryTreeFromOrderings(inorder+rootIndex+1,preorder+rootIndex+1,length-(rootIndex+1));//中序的后面一段长度作为需要寻找的字段长度
    cout<<node->data;
}

(2)已知中序后序求先序

struct TreeNode{//每一个节点的属性:左节点、右节点、数据
    int data;
    TreeNode* left;
    TreeNode* right;
};
void BinaryTreeFromOrderings(int *inorder,int *lastorder,int length)//中序序列,先序序列,中序中需要寻找的字段长度
{
    if(length==0)
        return;
    
    TreeNode* node=new TreeNode;
    node->data=lastorder+length-1;
    
    int rootindex;
    for(rootindex=0;rootindex<length;rootindex++)
        if(inorder[rootindex]==node->data)
            break;
    
    cout<<node->data<<" ";
    BinaryTreeFromOrderings(inorder,lastorder,rootindex);
    BinaryTreeFromOrderings(inorder+rootindex+1,lastorder+rootindex,length-(rootindex+1));
}



### 构建二叉树并获取后序遍历 通过遍历遍历构建二叉树的过程涉及递归方法的应用。遍历的第一个节点总是当前子树的根节点,在中遍历中找到这个根节点可以划分出左子树右子树,进而递归地处理这两个部分来重建整棵树[^1]。 #### 实现过程 对于给定的遍历`preorder`遍历`inorder`列: - 使用哈希表存储中遍历中的每个值及其对应的索引位置以便快速查找。 - 定义辅助函数用于递归建立左右子树,该函数接收四个参数:遍历起始点、遍历结束点、中遍历起始点以及中遍历结束点。 - 如果遍历起点超过终点,则返回空指针表示无更多节点可构造; - 否则取出当前子树的根节点(即遍历区间的首个元素),创建新的树节点; - 查找此根节点在中遍历的位置从而确定其左侧区间长度作为左子树大小,并据此调整后续递归调用时传递给下一层的边界条件; - 对于新创建的节点分别对其设置左右孩子为对应范围内的递归结果; - 返回新建好的节点对象供上级层连接成完整的树结构。 完成上述操作之后即可得到由输入列恢复出来的原始二叉树。最后再执行一次标准的后序遍历算法就能收集到所需的输出顺[^5]。 下面是具体的C++代码实现: ```cpp #include <unordered_map> using namespace std; struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; // Helper function to recursively construct the tree from pre-order and in-order traversals. TreeNode* buildTreeHelper(const vector<int>& preorder, const vector<int>& inorder, unordered_map<int, int>& indexMap, int pStart, int pEnd, int iStart, int iEnd) { if (pStart > pEnd || iStart > iEnd) return nullptr; // No more nodes to process. // The first node of current subtree is always root. int rootValue = preorder[pStart]; auto rootNode = new TreeNode(rootValue); // Find where this value appears within the in-order traversal list using our map. int idxInOrder = indexMap[rootValue]; // Calculate how many elements are on the left side of 'root' inside in-order sequence. int numLeftSubtreeElements = idxInOrder - iStart; // Recursively create children subtrees based upon updated indices after splitting at found position. rootNode->left = buildTreeHelper(preorder, inorder, indexMap, pStart + 1, pStart + numLeftSubtreeElements, iStart, idxInOrder - 1); rootNode->right = buildTreeHelper(preorder, inorder, indexMap, pStart + numLeftSubtreeElements + 1, pEnd, idxInOrder + 1, iEnd); return rootNode; } vector<int> postorderTraversal(TreeNode* root) { stack<TreeNode*> s; vector<int> result; TreeNode* lastVisitedNode = NULL; while (!s.empty() || root != NULL) { if (root != NULL) { s.push(root); root = root->left; } else { TreeNode* peekNode = s.top(); if (peekNode->right && lastVisitedNode != peekNode->right) { root = peekNode->right; } else { result.push_back(peekNode->val); lastVisitedNode = s.top(); s.pop(); } } } return result; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值