热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PATA1020——已知后序中序遍历求层序遍历

1020TreeTraversalsSupposethatallthekeysinabinarytreearedistinctpositivein
1020 Tree Traversals

Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, you are supposed to output the level order traversal sequence of the corresponding binary tree.

Input Specification:

Each input file contains one test case. For each case, the first line gives a positive integer N (30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are separated by a space.

Output Specification:

For each test case, print in one line the level order traversal sequence of the corresponding binary tree. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

Sample Output:

4 1 6 3 5 7 2
#include
#include
#include
#include
using namespace std;
const int maxn = 50;
struct node {
    int data;
    node *lchild;
    node *rchild;
};

int pre[maxn],in[maxn],post[maxn];  //先序、中序及后序
int n;  //结点个数

//当前二叉树的后序序列区间为[postL,postR],中序序列区间为[inL,inR]
//create函数返回构建出的二叉树的根节点地址
node* create(int postL, int postR, int inL, int inR) {
    if(postL > postR) {
        return NULL;    //若后序序列长度小于等于0,则直接返回
    }
    node* root = new node;  //新建一个新的结点,用来存取当前二叉树的根节点
    root->data = post[postR];   //新结点的数据域为根节点的值
    int k;
    for(k = inL; k <= inR; k++) {
        if(in[k] == post[postR]) {  //在中序序列中找到in[k] == pre[L]的结点
            break;
        }
    }
    int numLeft = k - inL;  //左子树的结点个数
    //返回左子树的根节点地址,赋值给root的左指针
    root->lchild = create(postL, postL+numLeft-1, inL, k-1);
    //返回右子树的根节点地址,赋值给root的右指针
    root->rchild = create(postL+numLeft, postR-1, k+1, inR);
    return root;    //返回根节点地址
}

int num = 0;    //已输出的结点个数
void BFS(node* root) {
    queue q;
    q.push(root);   //将根节点地址入队
    while(!q.empty()) {
        node* now = q.front();  //取出队首元素
        q.pop();
        printf("%d",now->data); //访问队首元素
        num++;
        if(num" ");
        if(now->lchild != NULL) q.push(now->lchild);
        if(now->rchild != NULL) q.push(now->rchild);
    }
}

int main() {
    scanf("%d", &n);
    for(int i=0; i) {
        scanf("%d", &post[i]);
    }
    for(int i=0; i) {
        scanf("%d", &in[i]);
    }
    node* root = create(0, n-1, 0, n-1);    //建树
    BFS(root);  //      层序遍历
    return 0;
}

 

四种基本的遍历思想为:

前序遍历:根结点 ---> 左子树 ---> 右子树

中序遍历:左子树---> 根结点 ---> 右子树

后序遍历:左子树 ---> 右子树 ---> 根结点

层次遍历:仅仅需按层次遍历就可以

求以下二叉树的各种遍历


前序遍历:1  2  4  5  7  8  3  6 

中序遍历:4  2  7  5  8  1  3  6

后序遍历:4  7  8  5  2  6  3  1

层次遍历:1  2  3  4  5  6  7  8

 

一.前序遍历

   前序遍历按照“根结点-左孩子-右孩子”的顺序进行访问。

   1.递归实现

1 void preOrder1(BinTree *root) //递归前序遍历 2 { 3 if(root!=NULL) 4  { 5 cout<data<<" "; 6 preOrder1(root->lchild); 7 preOrder1(root->rchild); 8  } 9 }

   2.非递归实现

  根据前序遍历访问的顺序,优先访问根结点,然后再分别访问左孩子和右孩子。即对于任一结点,其可看做是根结点,因此可以直接访问,访问完之后,若其左孩子不为空,按相同规则访问它的左子树;当访问其左子树时,再访问它的右子树。因此其处理过程如下:

  对于任一结点P:

     1)访问结点P,并将结点P入栈;

     2)判断结点P的左孩子是否为空,若为空,则取栈顶结点并进行出栈操作,并将栈顶结点的右孩子置为当前的结点P,循环至1);若不为空,则将P的左孩子置为当前的结点P;

     3)直到P为NULL并且栈为空,则遍历结束。

 1 void preOrder2(BinTree *root) //非递归前序遍历  2 {  3 stack s;  4 BinTree *p=root;  5 while(p!=NULL||!s.empty())  6  {  7 while(p!=NULL)  8  {  9 cout<data<<" "; 10  s.push(p); 11 p=p->lchild; 12  } 13 if(!s.empty()) 14  { 15 p=s.top(); 16  s.pop(); 17 p=p->rchild; 18  } 19  } 20 }

二.中序遍历

  中序遍历按照“左孩子-根结点-右孩子”的顺序进行访问。

  1.递归实现

1 void inOrder1(BinTree *root) //递归中序遍历 2 { 3 if(root!=NULL) 4  { 5 inOrder1(root->lchild); 6 cout<data<<" "; 7 inOrder1(root->rchild); 8  } 9 }

  2.非递归实现

  根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:

  对于任一结点P,

   1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;

   2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;

   3)直到P为NULL并且栈为空则遍历结束。

 1 void inOrder2(BinTree *root) //非递归中序遍历  2 {  3 stack s;  4 BinTree *p=root;  5 while(p!=NULL||!s.empty())  6  {  7 while(p!=NULL)  8  {  9  s.push(p); 10 p=p->lchild; 11  } 12 if(!s.empty()) 13  { 14 p=s.top(); 15 cout<data<<" "; 16  s.pop(); 17 p=p->rchild; 18  } 19  } 20 } 

三.后序遍历

       后序遍历按照“左孩子-右孩子-根结点”的顺序进行访问。

       1.递归实现

1 void postOrder1(BinTree *root) //递归后序遍历 2 { 3 if(root!=NULL) 4  { 5 postOrder1(root->lchild); 6 postOrder1(root->rchild); 7 cout<data<<" "; 8  } 9 }

  2.非递归实现

  后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问并且左孩子在右孩子前访问才能访问根结点,这就为流程的控制带来了难题。下面介绍两种思路。

      第一种思路:对于任一结点P,将其入栈,然后沿其左子树一直往下搜索,直到搜索到没有左孩子的结点,此时该结点出现在栈顶,但是此时不能将其出栈并访问, 因此其右孩子还为被访问。所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在栈顶,此时可以将其出栈并访问。这样就 保证了正确的访问顺序。可以看出,在这个过程中,每个结点都两次出现在栈顶,只有在第二次出现在栈顶时,才能访问它。因此需要多设置一个变量标识该结点是 否是第一次出现在栈顶。

 1 void postOrder2(BinTree *root) //非递归后序遍历  2 {  3 stack s;  4 BinTree *p=root;  5 BTNode *temp;  6 while(p!=NULL||!s.empty())  7  {  8 while(p!=NULL) //沿左子树一直往下搜索,直至出现没有左子树的结点  9  { 10 BTNode *btn=(BTNode *)malloc(sizeof(BTNode)); 11 btn->btnode=p; 12 btn->isFirst=true; 13  s.push(btn); 14 p=p->lchild; 15  } 16 if(!s.empty()) 17  { 18 temp=s.top(); 19  s.pop(); 20 if(temp->isFirst==true) //表示是第一次出现在栈顶 21  { 22 temp->isFirst=false; 23  s.push(temp); 24 p=temp->btnode->rchild; 25  } 26 else //第二次出现在栈顶 27  { 28 cout<btnode->data<<" "; 29 p=NULL; 30  } 31  } 32  } 33 }

  第二种思路:要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点P,先将其入栈。如果P不存在左孩子和右孩子,则可以直接访问它;或者P存 在左孩子或者右孩子,但是其左孩子和右孩子都已被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将P的右孩子和左孩子依次入栈,这样就保证了 每次取栈顶元素的时候,左孩子在右孩子前面被访问,左孩子和右孩子都在根结点前面被访问。

 1 void postOrder3(BinTree *root) //非递归后序遍历  2 {  3 stack s;  4 BinTree *cur; //当前结点  5 BinTree *pre=NULL; //前一次访问的结点  6  s.push(root);  7 while(!s.empty())  8  {  9 cur=s.top(); 10 if((cur->lchild==NULL&&cur->rchild==NULL)|| 11 (pre!=NULL&&(pre==cur->lchild||pre==cur->rchild))) 12  { 13 cout<data<<" "; //如果当前结点没有孩子结点或者孩子节点都已被访问过 14  s.pop(); 15 pre=cur; 16  } 17 else 18  { 19 if(cur->rchild!=NULL) 20 s.push(cur->rchild); 21 if(cur->lchild!=NULL) 22 s.push(cur->lchild); 23  } 24  } 25 }

 


推荐阅读
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了一个数列求和问题,该数列按照一定规律生成。通过观察数列的规律,我们可以得出求解该问题的算法。具体算法为计算前n项i*f[i]的和,其中f[i]表示数列中有i个数字。根据参考的思路,我们可以将算法的时间复杂度控制在O(n),即计算到5e5即可满足1e9的要求。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。 ... [详细]
  • 使用C++编写程序实现增加或删除桌面的右键列表项
    本文介绍了使用C++编写程序实现增加或删除桌面的右键列表项的方法。首先通过操作注册表来实现增加或删除右键列表项的目的,然后使用管理注册表的函数来编写程序。文章详细介绍了使用的五种函数:RegCreateKey、RegSetValueEx、RegOpenKeyEx、RegDeleteKey和RegCloseKey,并给出了增加一项的函数写法。通过本文的方法,可以方便地自定义桌面的右键列表项。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
author-avatar
波波利一_830
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有