1. 程式人生 > >PAT A1020——已知後序中序遍歷求層序遍歷

PAT A1020——已知後序中序遍歷求層序遍歷

返回 cstring test sca suppose lse pri end respond

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 }
技術分享圖片

PAT A1020——已知後序中序遍歷求層序遍歷