您的位置:首页 > 理论基础 > 数据结构算法

中国大学MOOC-陈越、何钦铭-数据结构 Tree Traversals Again

2015-10-19 19:18 736 查看
题目描述:

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3); pop(); pop(); push(4); pop(); pop(); push(5); push(6); pop(); pop(). Then a unique binary tree (shown in Figure 1) can be generated from this sequence of operations. Your task is to give the postorder traversal sequence of this tree.



Figure 1

Input Specification:

Each input file contains one test case. For each case, the first line contains a positive integer N (≤30) which is the total number of nodes in a tree (and hence the nodes are numbered from 1 to N). Then 2N lines follow, each describes a stack operation in the format: “Push X” where X is the index of the node being pushed onto the stack; or “Pop” meaning to pop one node from the stack.

Output Specification:

For each test case, print the postorder traversal sequence of the corresponding tree in one line. A solution is guaranteed to exist. All the numbers must be separated by exactly one space, and there must be no extra space at the end of the line.

Sample Input:

6

Push 1

Push 2

Push 3

Pop

Pop

Push 4

Pop

Pop

Push 5

Push 6

Pop

Pop

Sample Output:

3 4 2 6 5 1

思考:

这道题其实是利用树的先序,中序遍历结果来得到后续遍历结果。把输入的Push的元素按顺序输出就可以先序遍历结果。把Pop的元素按顺序输出就可以得到中序遍历结果。不过,因为在输入中Pop并没有输入数字,所以要利用Push的元素来得到Pop的结果。所以我的想法是利用一个数组,命名为Visited,大小和Push输入的大小一样,为N。Visited的作用是标记Push数组的元素是否被访问过,它的下标和Push数组的下标保持一致。同时Pop数组的下标和Push的下标保持一致,当输入为Pop时,记录下此时Pop数组的下标,并在Visited数组中从后往前查找最近一个未被访问过的Push元素,此时这个元素也是将要被Pop出来的元素。这就是得到中序遍历结果的方法。

接下来就是利用先序遍历,中序遍历得到后续遍历结果。对于先序遍历,它的第一个元素就是根节点,而对于后续遍历,它的最后一个元素才是根节点。所以我们就可以依次先得到根节点。接着我们可以去中序遍历数组的中找到这个根节点,并且根据中序遍历的特点可以得到,在中序遍历中,根节点左边的就是左子树,根节点右边的就是右子树。于是对于左右子树,我们便可以分而治之,也采用类似的方法,递归调用函数。

代码如下:

#include<iostream>
#include<string>
using namespace std;
#define MaxNumber 30//TreeNode数组所拥有的最大容量
typedef struct TreeNode *Order;
struct TreeNode{
string C;//记录输入的字符串值
int Key;//记录输入的节点值
};
TreeNode PreOrder[MaxNumber], InOrder[MaxNumber];//存储前序遍历,和中序遍历的节点信息
bool Visited[MaxNumber];//帮助获取前序遍历的信息
int PostOrder[MaxNumber];//后续遍历保存的信息
void Input(int N){
int i = -1, j ,l=-1,key;
string s;
for (int k = 0; k < 2 * N; k++){
cin >> s;
if (s == "Push"){//s==Push,把s和所输入的key存入到PreOrder数组中
cin >> key;
PreOrder[++i].C = s;
PreOrder[i].Key = key;
}
else{
if (s == "Pop"){//否则若为POP的话
j = i;//把当前PreOrder的下标赋给j
while (Visited[j] == true && j>-1){
if (j > 0){ j--; }//j>0才会执行j--
}   //j往前找,一直找到一个在PreOrder中未被访问的节点,这个节点就是当前要弹出的节点
InOrder[++l].C == "Pop";//于是执行赋值操作
InOrder[l].Key = PreOrder[j].Key;
Visited[j] = true;
}
}
}
}
void solve(int PreL, int InL, int PostL, int n){//PreL代表从PreOrder当前第一个节点的位置,InL,PostL同理。n代表传入的规模
int root, i;
int L, R;
if (n == 0){ return; }
if (n == 1){ PostOrder[PostL] = PreOrder[PreL].Key; return; }
root= PreOrder[PreL].Key;//找出当前的根节点
PostOrder[PostL + n - 1] = root;//把根节点插入到后序遍历的数组中
for (i = 0; i < n; i++){//找到在中序遍历数组中此时root位置
if (InOrder[InL+i].Key == root){ break; }
}
L = i;//L表示此时root左边节点个数
R = n - L - 1;//R表示此时root右边节点个数
solve(PreL + 1, InL, PostL, L);//分而治之,对左子树递归
solve(PreL + L + 1, InL + L + 1, PostL + L, R);//对右子树递归

}
int main(){
int N;
cin >> N;
Input(N);
solve(0, 0, 0, N);
for (int i = 0; i < N - 1; i++){
cout <<PostOrder[i] << " ";
}
cout << PostOrder[N-1];
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构