2001NOIP普及组真题 3. 求先序排列

线上OJ:

线上OJ:
【01NOIP普及组】求先序排列

核心思想:

1、先构建二叉树,再按照要求输出
2、构建的方法,可以使用字符数组,也可以使用字符串
3、构建树的核心是:通过递归,根据后序遍历和中序遍历构建树

第一步、后序遍历的最后一个一定是根 第二步、在中序遍历中找到根 第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归

传入参数说明:
int left_pos, int right_pos:待构建树的后序遍历的左右坐标
int left_mid, int right_mid:待构建树的中序遍历的左右坐标

解法一、使用字符数组 构建树

#include <bits/stdc++.h>
using namespace std;

const int N = 105;
char s1[N], s2[N];  // s1 中序遍历,s2 后序遍历 

struct Node
{
    char val;			// 存储当前节点的编号 
    int lchild, rchild;	// 存储当前节点的左子树和右子树节点的编号 
};
Node node[N];
int p = 0; 	// 存储节点编号 

/*
核心思想:通过递归,根据后序遍历和中序遍历创建树
 第一步、后序遍历的最后一个一定是根
 第二步、在中序遍历中找到根
 第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归 
 传入参数说明:
 int left_pos, int right_pos:待构建树的后序遍历的左右坐标
 int left_mid, int right_mid:待构建树的中序遍历的左右坐标
*/ 
int creatTree(int left_pos, int right_pos, int left_mid, int right_mid)
{
    if(left_pos > right_pos || left_mid > right_mid) 	// 判断递归退出边界 
        return 0;
	
    int id = ++p;	// 如果没越界,则为该节点创建新的编号
    node[id].val = s2[right_pos]; 	// 第一步、后序遍历的最后一个一定是根 
	
    // 第二步、在中序遍历中找到根。根左侧的都为左子树,右侧的都为右子树
    int len;	// 当找到根节点后,用于存储左子树的长度 
    int i; 		// 用于存储找到的根的下标 
    for(i = left_mid; i <= right_mid; i++)
    {
        if( s1[i] == s2[right_pos] ) // 在中序遍历中找根(根为后续遍历的最后一个) 
        {
            len = i - left_mid;  // i坐标前面的,是左子树的长度 
            break;
        }			
    } 	
	
    // 第三步、根左侧的都为左子树,右侧的都为右子树。对左子树和右子树分别再次递归。
    node[id].lchild = creatTree(left_pos, left_pos + len - 1, left_mid, i - 1);
    node[id].rchild = creatTree(left_pos + len, right_pos - 1, i + 1, right_mid);
	
    return id;
} 

void preOrder(int id)
{
    if(id == 0)   return;
    cout << node[id].val;
    preOrder(node[id].lchild);
    preOrder(node[id].rchild);
}

int main()
{
    cin >> s1 >> s2;
    int len_s1 = strlen(s1);
    int len_s2 = strlen(s2);
    int root = creatTree(0, len_s2 - 1, 0, len_s1 - 1);	// 创建树 
    preOrder(root);	// 后续遍历输出节点 	
}

解法二、使用字符串 构建树

#include <bits/stdc++.h>
using namespace std;

const int N = 105;
string s1, s2;  // s1 中序遍历,s2 后序遍历 

struct Node
{
    char val;			// 存储当前节点的编号 
    int lchild, rchild;	// 存储当前节点的左子树和右子树节点的编号 
};
Node node[N];
int p = 0;	// 存储节点编号

int createTree(string sp, string si)//用后序序列sp与中序序列si构建二叉树,返回树根 
{
    int id = ++p;
    node[id].val = sp[sp.length()-1];
    
    int i = si.find(node[id].val);		// 在中序遍历中找到根,利用 string的 s.find() 函数即可,返回即为索引 
            
    int len_l = i, len_r = si.length() - 1 - i;//左右子树序列长度 
    
    //序列长度大于0,才可以建立一棵树
    if(len_l > 0)  node[id].lchild = createTree(sp.substr(0, len_l), si.substr(0, len_l));
    if(len_r > 0)  node[id].rchild = createTree(sp.substr(len_l, len_r), si.substr(len_l + 1, len_r));
		
    return id;
}

void preOrder(int id)
{
    if(id == 0)   return;
    cout << node[id].val;
    preOrder(node[id].lchild);
    preOrder(node[id].rchild);
}

int main()
{
    cin >> s1 >> s2;
    int root = createTree(s2, s1);
    preOrder(root);
    return 0;
}

解法三、不构建树,直接输出

#include <bits/stdc++.h>
using namespace std;

string s1, s2;  // s1 中序遍历,s2 后序遍历 

void calc(int left_pos, int right_pos, int left_mid, int right_mid)
{
    cout << s2[right_pos];  // 这一行的位置决定了输出结果是先序遍历还是后序遍历
    int i = s1.find(s2[right_pos]);		// 第一步、后序遍历的最后一个一定是根。根据这个“根”,在中序遍历中找到其坐标 i 
    int len = i - left_mid;
    
    // 在中序遍历中,如果 i 不是左边界(即 i > left_mid),则说明有左子树。需要进行递归
    if(i > left_mid) calc(left_pos, left_pos + len - 1, left_mid, i - 1);	
    
    // 在中序遍历中,如果 i 不是右边界(即 i < right_mid),则说明有右子树。需要进行递归 
    if(i < right_mid) calc(left_pos + len, right_pos - 1, i + 1, right_mid);

    // cout << s2[right_pos];  // 这一行的位置决定了输出结果是先序遍历还是后序遍历。放在此处就是后序遍历
}

int main()
{
    cin >> s1 >> s2;
    calc(0, s2.length() - 1, 0, s1.length() - 1);
    cout << endl;
    return 0;
}    
  • 13
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值