PAT A1020——已知后序中序遍历求层序遍历

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<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
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<node*> q;
    q.push(root);   //将根节点地址入队
    while(!q.empty()) {
        node* now = q.front();  //取出队首元素
        q.pop();
        printf("%d",now->data); //访问队首元素
        num++;
        if(num<n) printf(" ");
        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<n; i++) {
        scanf("%d", &post[i]);
    }
    for(int i=0; i<n; 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<<root->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<BinTree*> s;
 4     BinTree *p=root;
 5     while(p!=NULL||!s.empty())
 6     {
 7         while(p!=NULL)
 8         {
 9             cout<<p->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<<root->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<BinTree*> 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<<p->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<<root->data<<" ";
8     }    
9 }
复制代码

  2.非递归实现

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

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

复制代码
 1 void postOrder2(BinTree *root)    //非递归后序遍历
 2 {
 3     stack<BTNode*> 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<<temp->btnode->data<<" ";
29                 p=NULL;
30             }
31         }
32     }    
33 }
复制代码

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

复制代码
 1 void postOrder3(BinTree *root)     //非递归后序遍历
 2 {
 3     stack<BinTree*> 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<<cur->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 }
复制代码

 

原文地址:https://www.cnblogs.com/mxj961116/p/10441120.html