代码实现:已知后序和中序遍历次序,求先序遍历次序

阅读需知:

1.至少需要了解前、中、后三种遍历,函数递归与堆栈;

2.下文中的“根”结点指树的根结点与“子根结点”(作者自己的叫法),“子根节点”指各种子树的根结点,可以简单理解为除了根节点外的所有结点。

在前、中、后三个遍历次序中,我们可以发现始终是左孩子比右孩子先出,左子树比右子树先出,

而区别则在于“根”节点输出位置不同。

(1)中序遍历的一个好处是让我们明晰了左右子树各自的结点数目。

(2)而前后遍历在“根”节点的输出上完全相反,除非遇见了叶结点。

根据(1)我们可以知道各个子树的结点数目,即各个子区间范围

根据(2)我们可以取各个子区间的最后一位存储进新数组,从而实现后序变先序。

代码如下所示:

(数组均采用了动态数组,亦可改用为足够大的数组;getchar()是为了吃缓存,即吃enter)

#include<stdio.h>
#include<stdlib.h>
struct Stack {
    int ra, la,rb,rl;
    Stack* pre_QJ;
};
struct Stack* front=NULL;
char* a, * b,*c,*s;
int i;  
void funcation1(int ra,int la,int rb,int lb);
void funcation2(int ra, int la, int rb, int lb);
Stack* push(Stack* new_node);
Stack* pop(void);
int isempty();


//主函数
int main() {
    int n;
    printf("请输入树的结点个数:");
    scanf("%d",&n);
    printf("请输入树的中序遍历结果:");
    a = (char *)malloc(sizeof(char) * n);
    getchar();
    for (i = 0; i < n; i++) {
        scanf("%c", &a[i]);
    }
    printf("请输入树的后序遍历结果:");
    b = (char*)malloc(sizeof(char)*n);
    getchar();
    for (i = 0; i < n;i++) {
        scanf("%c",&b[i]);
    }
    i = 0;
    c = (char*)malloc(sizeof(char) * n);
    funcation1(0,n-1,0,n-1);
    printf("其先序遍历的结果为(递归):");
    for (i = 0; i < n; i++) {
        printf("%c",c[i]);
    }
    printf("\n");
    i = 0;
    s = (char*)malloc(sizeof(char) * n);
    printf("其先序遍历的结果为(非递归):");
    funcation2(0, n - 1, 0, n - 1);
    for (i = 0; i < n; i++) {
        printf("%c", s[i]);
    }
    return 0;
}

//递归版
void funcation1(int ra, int la, int rb, int lb){
    if (lb < rb||la<ra)return;//退出递归条件,记为A
    c[i++] = b[lb];
    int m = ra;
    while (a[m] != b[lb]) {
        if (m < la)m++;
        else break;
    }
    int d = la - m;
    funcation1(ra,m-1,rb,lb-d-1);
    funcation1(m + 1, la, lb - d, lb - 1);
}

//非递归版,写出递归版我们发现需要堆栈存储两个数组的区间
void funcation2(int ra, int la, int rb, int lb) {
    int m;
    while (1) {
        while (ra<=la&&rb<=lb) {//进行条件是递归版退出递归条件的反逻辑值,即为!A
            s[i++] = b[lb];//后序遍历根后出,先序遍历根先出...故此将后序遍历尾值置于首位
            m = ra;
            while (m < la && a[m] != b[lb]) {
                m++;
            }//在中序遍历中寻找根值所在位置,为接下来分割区间做准备
            Stack* new_node;
            new_node = (Stack*)malloc(sizeof(Stack));
            //创建新记录条目。
            
            int d = la - m;//表示右子树的节点数目
            new_node->ra = m + 1; new_node->la = la; 
            new_node->rb = lb - d; new_node->rl = lb - 1;
            //记录右子树在中序与后序数组的对应区间;

            front = push(new_node);//将新记录条目存入堆栈

            ra = ra; la = m - 1; rb = rb; lb = lb - d - 1;//设置处理左子树相应区间
        }
        if (!isempty()) {//堆栈不空则重置两个数组所需查找区间范围,并抛出栈堆值
            ra = front->ra;
            rb = front->rb;
            la = front->la;
            lb = front->rl;
            pop();
        }
        else break;//为空则退出内层循环
    }
    return;//退出外层循环
}


Stack* push(Stack* new_node) {
    new_node->pre_QJ = front;
    front = new_node;
    return front;
}


Stack* pop(void) {
    Stack* temp;
    if (!isempty()) {
        temp = front;
        front = front->pre_QJ;
        free(temp);
    }
    return front;
}


int isempty() {
    if (front == NULL)return 1;
    else return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值