用Tree 完成 后序变中序

本文介绍了一种使用栈来辅助构建表达式树的方法,并实现了中序遍历以输出表达式的功能。通过定义树节点结构及栈操作,程序能够处理输入的字符表达式,创建对应的表达式树。

#include <stdio.h>
#include <conio.h>


typedef struct tn
{
    char info;
    struct tn *LChild;
    struct tn *RChild;

}TreeNode, *Tree;

typedef struct node
{
    Tree ptr;
    struct node *link;
}Node,*Stack;

void InitStack(Stack *p)
{
  Stack q;
  q =(Stack)malloc(sizeof(Node));
  q->ptr=NULL;
  q->link=NULL;
  *p = q;
  return;
}

void Push(Stack H,Tree x)
{
  Stack q;
  q =(Stack)malloc(sizeof(Node));
  q->ptr=x;
  q->link=H->link;
  H->link=q;
  return;

}

void Pop(Stack H)
{
   Stack q;
  q = H->link;
  H->link=H->link->link;
  free(q);
  return;

}

Tree Top(Stack H)
{
   return H->link->ptr;
}

int EmptyStack(Stack H)
{
  if(H->link==NULL)
    return 1;
  else
    return 0;
}


Tree CreateTree(int x, Tree lchild, Tree rchild)
{
    Tree t;
    t=(Tree)malloc(sizeof(TreeNode));
    t->info=x;
    t->LChild=lchild;
    t->RChild=rchild;
    return t;

}

Tree ValueTree(char *expr)
{
    Stack S;
    Tree p,t1,t2;

    InitStack(&S);
    while(*expr!=NULL)
    {
        if( (*expr<'z') && (*expr>'a'))
        {

            p=CreateTree(*expr,NULL,NULL);
            Push(S,p);
         }
        else
        {
            t1=Top(S);
            Pop(S);
            t2=Top(S);
            Pop(S);
            p= CreateTree(*expr,t2,t1);
            Push(S,p);


        }
        expr++;
    }
    return p;
}
void PrintTreeInOrder(Tree T)
{
    if(T!=NULL)
    {

        if(T->LChild!=NULL)
            printf("(");
        PrintTreeInOrder(T->LChild);

        printf("%c", T->info);
        PrintTreeInOrder(T->RChild);
        if(T->RChild!=NULL )
            printf(")");


    }
    return;
}

void main()
{
    char expression[]="abc*+de*f+g*+";
    PrintTreeInOrder(ValueTree(expression));

    getch();
    return;
}

<think> 我们已知二叉树的后序遍历(左子树 -> 右子树 -> 根节点)和中遍历(左子树 -> 根节点 -> 右子树),要求前遍历(根节点 -> 左子树 -> 右子树)。 核心思想:利用后序遍历确定根节点,然后在中遍历中定位根节点,从而划分左右子树,递归处理。 步骤: 1. 后序遍历的最后一个元素是当前二叉树的根节点。 2. 在中遍历中找到根节点的位置,则根节点左侧是左子树的中遍历,右侧是右子树的中遍历。 3. 根据中遍历中左右子树的节点数量,可以在后序遍历中划分出左右子树的后序遍历(左子树的后序遍历在后序遍历数组中连续出现,紧接着是右子树,最后是根节点)。 4. 递归处理左子树和右子树。 具体算法(递归函数): 假设: post: 后序遍历数组,postStart和postEnd分别是当前子树在后序遍历数组中的起始和结束下标。 in: 中遍历数组,inStart和inEnd分别是当前子树在中遍历数组中的起始和结束下标。 递归函数的功能:根据给定的后序和中列,重构二叉树并输出前遍历列(或者存储前遍历列)。 注意:我们并不需要真正构建二叉树节点,只需要输出前遍历列即可。 递归步骤: 1. 如果当前子树为空(inStart > inEnd 或 postStart > postEnd),则返回。 2. 当前根节点:post[postEnd]。 3. 输出根节点(或者存储,因为前遍历先访问根)。 4. 在中遍历数组中找到根节点的位置,记为index。则: 左子树的中遍历:inStart 到 index-1 右子树的中遍历:index+1 到 inEnd 5. 计算左子树节点个数:leftSize = index - inStart 6. 左子树的后序遍历:postStart 到 postStart+leftSize-1 右子树的后序遍历:postStart+leftSize 到 postEnd-1 (因为最后一个节点是根) 7. 递归处理左子树:传入左子树的后序和中范围 8. 递归处理右子树:传入右子树的后序后序范围 但是注意:在递归左右子树之前,我们已经输出了根节点(前遍历的顺:根->左->右),所以先输出根节点,然后递归左子树,再递归右子树。 然而,如果我们只需要得到前遍历列,那么按照上述顺输出即可。 具体代码实现(Java): 假设中遍历数组为inOrder,后序遍历数组为postOrder。 递归函数定义: void buildTree(int[] inOrder, int[] postOrder, int inStart, int inEnd, int postStart, int postEnd) 调用方式:给定整个中后序数组,以及初始范围: inStart=0, inEnd=inOrder.length-1 postStart=0, postEnd=postOrder.length-1 递归内部: 根节点值:int rootVal = postOrder[postEnd]; 输出根节点:System.out.print(rootVal + " "); // 或者加入列表 在中数组中找到rootVal的位置index(注意:假设节点值唯一) 左子树节点数:int leftSize = index - inStart; 右子树节点数:int rightSize = inEnd - index; 左子树: 中范围:inStart 到 index-1 后序范围:postStart 到 postStart+leftSize-1 右子树: 中范围:index+1 到 inEnd 后序范围:postStart+leftSize 到 postEnd-1 然后递归: buildTree(inOrder, postOrder, inStart, index-1, postStart, postStart+leftSize-1); // 左子树 buildTree(inOrder, postOrder, index+1, inEnd, postStart+leftSize, postEnd-1); // 右子树 注意:递归调用前我们先输出了根节点,所以整个列就是前遍历。 但是,如果树为空(inStart>inEnd)则返回。 另外,在递归左右子树之前输出根节点,这样符合前遍历顺。 举例验证: 后序:4 5 2 6 7 3 1 中:4 2 5 1 6 3 7 第一步:后序最后一个1是根节点。 中中1的位置是3(下标从0开始),则: 左子树中:4 2 5 -> 下标0~2 右子树中:6 3 7 -> 下标4~6 左子树节点数=3,所以后序中左子树的后序列:前3个(4,5,2)-> 对应下标0~2 右子树的后序列:接着的3个(6,7,3)-> 下标3~5(注意后序最后一个1是根,所以右子树的后序是下标3到5) 然后递归: 左子树:中[0:2] = [4,2,5];后序[0:2] = [4,5,2] -> 左子树的根是2(后序最后一个元素) 右子树:中[4:6] = [6,3,7];后序[3:5] = [6,7,3] -> 右子树的根是3 输出顺:先输出根1,然后递归左子树输出左子树的根2,再递归左子树的左子树(中4,后序4)输出4,然后左子树的右子树(中5,后序5)输出5,然后再递归右子树输出3,再递归右子树的左子树(6)输出6,右子树的右子树(7)输出7?不对,右子树的中是6,3,7,根是3,3的左子树是6,右子树是7。 所以前输出:1,2,4,5,3,6,7 但标准的前应该是:1,2,4,5,3,6,7 因此,完整代码: 注意:需要处理中中查找根节点位置,我们可以提前用HashMap存储中遍历的值和索引,避免每次循环查找。 优化:使用HashMap记录中遍历每个值对应的索引,这样查找根节点位置只需要O(1)时间。 代码实现(Java): ```java import java.util.*; public class TreeTraversal { private Map<Integer, Integer> inOrderIndexMap = new HashMap<>(); public void buildTree(int[] inOrder, int[] postOrder) { // 构建中遍历的索引映射 for (int i = 0; i < inOrder.length; i++) { inOrderIndexMap.put(inOrder[i], i); } buildTreeHelper(inOrder, postOrder, 0, inOrder.length - 1, 0, postOrder.length - 1); } private void buildTreeHelper(int[] inOrder, int[] postOrder, int inStart, int inEnd, int postStart, int postEnd) { if (inStart > inEnd || postStart > postEnd) { return; } // 根节点是后序遍历的最后一个元素 int rootVal = postOrder[postEnd]; // 输出根节点(前遍历) System.out.print(rootVal + " "); // 在中遍历中找到根节点的位置 int rootIndex = inOrderIndexMap.get(rootVal); int leftSize = rootIndex - inStart; // 左子树节点个数 // 递归构建左子树 // 左子树的中遍历区间: [inStart, rootIndex-1] // 左子树的后序遍历区间: [postStart, postStart+leftSize-1] buildTreeHelper(inOrder, postOrder, inStart, rootIndex - 1, postStart, postStart + leftSize - 1); // 递归构建右子树 // 右子树的中遍历区间: [rootIndex+1, inEnd] // 右子树的后序遍历区间: [postStart+leftSize, postEnd-1] buildTreeHelper(inOrder, postOrder, rootIndex + 1, inEnd, postStart + leftSize, postEnd - 1); } public static void main(String[] args) { TreeTraversal solution = new TreeTraversal(); int[] inOrder = {4, 2, 5, 1, 6, 3, 7}; int[] postOrder = {4, 5, 2, 6, 7, 3, 1}; solution.buildTree(inOrder, postOrder); // 输出前遍历: 1 2 4 5 3 6 7 } } ``` 运行结果:1 2 4 5 3 6 7 注意:这个递归函数直接打印前遍历列,如果需要返回列表,可以改为将节点值加入列表。 修改为返回列表的版本: ```java public List<Integer> getPreOrder(int[] inOrder, int[] postOrder) { // 构建中遍历的索引映射 for (int i = 0; i < inOrder.length; i++) { inOrderIndexMap.put(inOrder[i], i); } List<Integer> preOrderList = new ArrayList<>(); buildTreeHelper(inOrder, postOrder, 0, inOrder.length - 1, 0, postOrder.length - 1, preOrderList); return preOrderList; } private void buildTreeHelper(int[] inOrder, int[] postOrder, int inStart, int inEnd, int postStart, int postEnd, List<Integer> preOrderList) { if (inStart > inEnd || postStart > postEnd) { return; } int rootVal = postOrder[postEnd]; preOrderList.add(rootVal); int rootIndex = inOrderIndexMap.get(rootVal); int leftSize = rootIndex - inStart; buildTreeHelper(inOrder, postOrder, inStart, rootIndex - 1, postStart, postStart + leftSize - 1, preOrderList); buildTreeHelper(inOrder, postOrder, rootIndex + 1, inEnd, postStart + leftSize, postEnd - 1, preOrderList); } ``` 这样调用getPreOrder方法即可返回前遍历的链表。 总结:已知后序遍历和中遍历求前遍历,通过递归划分左右子树,并按照前遍历顺(根、左、右)将根节点加入结果,递归左右子树即可。 注意:该方法假设二叉树节点值唯一,且输入的后序和中遍历列是同一棵二叉树的合法列。 引用:后序遍历的顺是左子树->右子树->根节点,中遍历的顺是左子树->根节点->右子树[^1]。通过后序遍历的最后一个元素确定根节点,然后在中遍历中定位根节点,从而划分左右子树[^2][^3]。 </think> ### 根据后序遍历和中遍历求前遍历的方法 #### 核心原理 后序遍历(左→右→根)和中遍历(左→根→右)的组合可唯一确定二叉树结构: 1. **后序末元素为根节点** 2. **在中中找到根节点位置**: - 左侧为左子树的中列 - 右侧为右子树的中列 3. **递归处理子树**: - 左子树节点数 = 中根节点左侧元素数 - 右子树节点数 = 中根节点右侧元素数 #### 算法步骤(Java实现) ```java import java.util.*; class Solution { private Map<Integer, Integer> inOrderMap = new HashMap<>(); // 主函数:输入后序和中,返回前列 public List<Integer> getPreOrder(int[] inOrder, int[] postOrder) { // 构建中遍历的索引映射(值->位置) for (int i = 0; i < inOrder.length; i++) { inOrderMap.put(inOrder[i], i); } List<Integer> preOrder = new ArrayList<>(); buildTree(inOrder, postOrder, 0, inOrder.length - 1, 0, postOrder.length - 1, preOrder); return preOrder; } private void buildTree(int[] in, int[] post, int inStart, int inEnd, int postStart, int postEnd, List<Integer> preOrder) { if (inStart > inEnd) return; // 后序末元素是当前根节点 int rootVal = post[postEnd]; // 前:先添加根节点 preOrder.add(rootVal); // 定位根节点在中的位置 int rootIdx = inOrderMap.get(rootVal); int leftSize = rootIdx - inStart; // 左子树节点数 // 递归构建左子树 (前:根->左->右) buildTree(in, post, inStart, rootIdx - 1, postStart, postStart + leftSize - 1, preOrder); // 递归构建右子树 buildTree(in, post, rootIdx + 1, inEnd, postStart + leftSize, postEnd - 1, preOrder); } } ``` #### 执行过程示例 输入: - 中遍历 = `[9,3,15,20,7]` - 后序遍历 = `[9,15,7,20,3]` 步骤: 1. 后序末元素 `3` 是根节点 2. 在中定位 `3`: - 左子树中:`[9]`(1节点) - 右子树中:`[15,20,7]`(3节点) 3. 递归左子树: - 后序:`[9]` → 根节点 `9` 4. 递归右子树: - 后序末 `20` 为根节点 - 中划分:左 `[15]`,右 `[7]` 5. 前输出列:`[3,9,20,15,7]` 时间复杂度:$O(n)$,空间复杂度:$O(n)$(哈希表存储中索引)[^1][^2][^3] #### 关键总结 | 操作 | 关键步骤 | |----------------|--------------------------------------| | 根节点定位 | 后序遍历的最后一个元素 | | 子树划分 | 中遍历中根节点分隔左右子树 | | 子树节点计算 | 左子树节点数 = `rootIdx - inStart` | | 递归边界 | `inStart > inEnd` 时终止 | > 此方法适用于所有二叉树结构(包括非完全二叉树),且能保证重建结果的唯一性。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值