(新版)SJTU-OJ-1049. 二哥学二叉树

题目描述

二哥学了二叉树的顺序存储后,被下面一个问题难住了,于是他请你帮他解决。给你一个前序遍历和中序遍历,问顺序存储的数组是什么样子的。
Example

输入格式

第一行为前序遍历,第二行为中序遍历,节点个数不超过26.

输出格式

输出一行,表示顺序存储的数组,以空格隔开,NULL表示空节点,数组空间不超过1000个节点.

样例输入

ABCD BADC

样例输出

A B C NULL NULL D

数据范围

(无)

题目分析

      你有没有发现SJTU的OJ有一个二哥还有一个小可怜经常出题?二哥摘苹果,二哥种花生,二哥养兔子……
      这个题目需要的知识有函数的递归,二叉树的基本概念,前序遍历、中序遍历。如果没有学过可以看看数据结构的教材《数据结构:思想与实现(翁慧玉 俞勇)》- 第六章 树。只需要知道概念即可,可以看一下教材的P162-164,看完你就会做这个题目(确信)。
      程序设计的课程就学二叉树ACM班也太猛了吧! 不过我想趁这个题目复习一下字符串的知识,还有了解一下二叉树的基本知识。

二叉树

      前序遍历: 先访问根节点,然后前序遍历左子树,前序遍历右子树
      中序遍历: 先中序访问左子树,再访问根节点,最后中序遍历右子树
      后序遍历: 先中序访问左子树,再中序遍历右子树,最后访问根节点
      一句话总结:前序、中序、后序指的是 根节点 在访问顺序的排序!

      编号问题: 计算公式如下 (注意:树的根结点编号为 1) 右 儿 子 编 号 = 根 节 点 编 号 × 2 右儿子编号=根节点编号\times2 =×2 左 儿 子 编 号 = 根 节 点 编 号 × 2 + 1 左儿子编号=根节点编号\times2+1 =×2+1
二叉树编号

字符串

      (转载)字符串知识传送门,想看原版的可以走传送门,不想走的可以接着往下看,内容差不多。个人觉得要用的时候看也可,反正不是经常用的玩野。

读取方法:
编号读取方法读取特点
方法一cin接受一个字符串,遇“空格”、“Tab”、“回车”都结束
方法二cin.get()用法一:cin.get(字符变量名)可以用来接收字符
用法二:cin.get(字符数组名,接收字符数)用来接收一行字符串,可以接收空格
用法三:cin.get(无参数)没有参数主要是用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补cin.get(字符数组名,接收字符数目)的不足。
方法三cin.getline()cin.getline() ----接受一个字符串,可以接收空格并输出
方法四getline()getline() // 接受一个字符串,可以接收空格并输出,需包含#include<string>
方法五gets()gets()// 接受一个字符串,可以接收空格并输出,需包含#include<string>
方法六getchar()getchar()//接受一个字符
长度获取:
  • 加上头文件:#include <string.h>
  • 定义一个字符数组 char pre[50];
  • 输入数据的时候可以这样 cin >> pre;
  • strlen(数组名);函数将会返回一个整数,表示一个字符数组内字符个数

题目解答

      老套路,从我们之前写的就可以看出来:

前序遍历: 先访问根节点,然后前序遍历左子树,前序遍历右子树

      前序遍历里面套娃前序遍历,明显是要递归解决!
      递归的核心:函数之间的通讯内容 [传递的信息] ,终止条件。
      我们不妨还是用课本的案例来:

课本案例:
已知一个二叉树的前序遍历,中序遍历,求这个二叉树:
二叉树前序遍历序列是:A、L、B、E、C、D、W、X
二叉树中序遍历序列是:B、L、E、A、C、W、X、D

  1. 由前序遍历序列找根节点,显然是 A
  2. 由中序遍历划分根节点左边的元素、右边的元素(具体方法:在中序遍历中找根节点A,根节点A左边的就是 根节点的左子树里面的元素,右边的是 根节点的右子树里面的元素
  3. 套娃循环,分别对 根节点的左子树里面的元素根节点的右子树里面的元素 重复递归,执行步骤一、二,直到每个元素位置确定!

      回归话题:函数之间的通讯内容有:节点的编号、当前查找的序列在前序遍历数组的起点、终点、在中序遍历的起点、终点,终止条件:当前查找的元素只有一个!此时就要确定位置,位置就是当前节点的编号,上代码!

#include <iostream>
#include <string.h>
using namespace std;
char pre[50];		// 前序遍历序列
char in[50];		// 中序遍历序列
char ans[1050];		// 答案数组

// 把中序遍历分割为左右两半
// 使用方法(0,num-1,0,num-1,1);
void solution(int prest,int preed,int inst,int ined,int rootnum)
{
    ans[rootnum] = pre[prest];	// 把根节点传入答案数组
    	
	// 当前查找的元素只有一个!此时就要向答案数组里面传递信息,位置就是当前节点的编号 rootnum
    if (prest == preed && inst == ined)
    {
        ans[rootnum] = pre[prest];
        return;
    }
    
    // 当前查找的元素超过一个,继续二分!
    else
    {
        int targetnum;			
        for (int i = inst; i <= ined; i++)
        {
            if (in[i] == pre[prest])
            {
                targetnum = i;
                break;
            }	
            // 在中序遍历中找根节点A,便于后续划分
        }

        if (targetnum > inst)
        	// 下面这个函数递归的是左子树的元素 2 * rootnum是左儿子的编号
            solution(prest + 1, prest + targetnum - inst, inst, targetnum - 1, 2 * rootnum);
        if (targetnum < ined)
        	// 下面这个函数递归的是右子树的元素 2 * rootnum + 1是右儿子的编号
            solution(preed - ined + targetnum + 1, preed, targetnum + 1, ined, 2 * rootnum + 1);
    }
}  

int main()
{
    cin >> pre;
    cin >> in;
    int num = strlen(pre);  // 获取字符串的长度,也可以用下面的注释的方法
    // while (pre[num] >= 'A' && pre[num]<='Z')
    // {
    //     num++;
    // }
    // 此时num反映了输入的字符串个数!

    solution(0, num - 1, 0, num - 1, 1);
	
	// 去掉答案数组里无效的元素!
    int p = 1049;
    while (ans[p] < 'A' || ans[p] > 'Z')
    {
        p--;
    }
	
	// 输出答案!
    for (int i = 1; i <= p;i++)
    {
        if (ans[i] >= 'A' && ans[i] <= 'Z')
            cout << ans[i] << " ";
        else
            cout << "NULL" << " ";
    }
        system("pause");
    return 0;
}

      一些其他的解法,可以参考,来源SJTU 解答集 OJ,方法各有千秋。

/* 二哥学二叉树 */
#include <iostream>
#include <cstring>
using namespace std;

char a[1005];
char dlr[30], ldr[30];

void restoreTree(char *dlr, char *ldr, int len, int root){
    if (len <= 0)
        return;
    char r = dlr[0];
    a[root] = r;
    int i = 0;
    while (ldr[i] != r)
        ++i;
    restoreTree(dlr + 1, ldr, i, root * 2);
    restoreTree(dlr + i + 1, ldr + i + 1, len - i - 1, root * 2 + 1);
}

int main(){
    cin >> dlr >> ldr;
    for (int i = 1; i <= 1004; ++i)
        a[i] = ' ';
    restoreTree(dlr, ldr, strlen(dlr), 1);
    int n = 1000;
    while (a[n] == ' ')
        --n;
    for (int i = 1; i <= n; ++i){
        if (a[i] == ' ')
            cout << "NULL ";
        else
            cout << a[i] << ' ';
    }
    cout << endl;
    return 0;
}
#include "iostream"
#include "cmath"
#include "cstring"
using namespace std;

//二叉树类
template <class T>
class myBinaryTree {
public:
    class node {
    public:
        T data;
        T *lchild=nullptr, *rchild=nullptr;
    };
    node *root=nullptr;

    //清空
    void clear() {
        clear(root);
    }

    void clear(node *p) {
        if (p == nullptr)
            return;
        clear(p->lchild);
        clear(p->rchild);
        delete p;
        p = nullptr;
    }

    //构造
    myBinaryTree(){}

    //析构
    ~myBinaryTree() {
        clear(root);
    }

    //判断是否非空
    bool empty() {
        return root == nullptr;
    }
};

char tdata[1009] = { 0 };
int dataSize = 0;

void createTree(int rootNum, char *tempData1,char *tempData2) {
    if (strlen(tempData1) == 0) return;
    char root = tempData1[0];
    tdata[rootNum] = root;
    dataSize = rootNum < dataSize ? dataSize : rootNum;
    char ldata1[50] = { 0 }, rdata1[50] = { 0 }, ldata2[50] = { 0 }, rdata2[50] = { 0 };
    int length = strlen(tempData1);
    bool flag = false;
    for (int i = 0,k=0; i < length; i++,k++) {
        if (tempData2[i] == root) {
            flag = true;
            k = -1;
        }
        else if (!flag) {
            ldata1[k] = tempData1[i + 1];
            ldata2[k] = tempData2[i];
        }
        else if (flag) {
            rdata1[k] = tempData1[i];
            rdata2[k] = tempData2[i];
        }
    }
    //递归
    createTree(rootNum * 2, ldata1, ldata2);
    createTree(rootNum * 2 + 1, rdata1, rdata2);
}

int main() {
    char preData[50], postData[50];
    cin >> preData >> postData;

    createTree(1, preData, postData);

    //输出
    cout << tdata[1];
    for (int i = 2; i <= dataSize; i++) {
        if (tdata[i] == 0)
            cout << " NULL";
        else
            cout << " " << tdata[i];
    }

    return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
char pre[100],in[100],ans[2000];
int maxn;
void dfs(int ps,int pe,int is,int it,int p)
{
    int root,leftlen,rightlen;
    for (root=is;root<it;++root){
        if (in[root]==pre[ps])
            break;
    }
    ans[p]=in[root];
    if (p>maxn) maxn=p;
    leftlen=root-is;
    if (leftlen>0) dfs(ps+1,ps+leftlen,is,root,p*2);
    rightlen=it-root-1;
    if (rightlen>0) dfs(ps+leftlen+1,pe,root+1,it,p*2+1);
}
int main() {
    cin>>pre;
    cin>>in;
    dfs(0,strlen(pre),0,strlen(in),1);
    for (int i=1;i<=maxn;++i)
        if (ans[i]) cout<<ans[i]<<" ";
        else cout<<"NULL ";
    return 0;
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值