已知中序遍历的栈的操作序列,唯一确定一棵二叉树。请编程输出该二叉树的后序遍历序列。
第一行一个整数n,表示二叉树的结点个数。 接下来2n行,每行描述一个栈操作,格式为:push X 表示将结点X压入栈中,pop 表示从栈中弹出一个结点。 (X用一个字符表示)
提示:仔细分析二叉树非递归遍历过程中栈的操作规律与遍历序列的关系,可将二叉树构造出来。
输入:
6
push a
push b
push c
pop
pop
push d
pop
pop
push e
push f
pop
pop
输出:
cdbfea
分析:
这道题目是通过中序非递归遍历的栈的操作顺序来确定整棵二叉树的结构。
首先我们要理解中序非递归遍历是什么?
中序非递归遍历:
void InOrder(BiTree T)
{
stack<BiTree> st;
BiTree P = T;
while (P || !st.empty()) // 如果P不为空并且 st不为空
{
if (P) {// 找到P为根节点的最左子树,
st.push(P);
P = P->lchild;
}
else//P为叶子节点时
{
P = st.top();//P指向的是没有左孩子或者以及访问了左孩子但是没有访问右孩子的点
st.pop();
cout << P->val << endl;
P = P->rchild;
}
}
}
可以看到首先我们要找到第一个节点,沿着左链走,第一个没有左孩子的点。
可以看出pop掉的是没有左孩子或者以及访问了左孩子 没有访问右孩子的点。
idea:
1. 数据结构:栈
2. 基本思路:
1. 构建一个栈
2. 先将根节点push到栈里面
3. 如果push前面还是push说明此时入栈的节点是前一个节点的左孩子
4. 如果push前面是pop说明此时入栈的节点是前一个节点的右孩子
5. 如果是pop怎么解决呢?
和中序遍历的非递归算法一样,更新p的指向
完整代码:
#include <iostream>
#include <stack>
using namespace std;
typedef char elemtype;
typedef struct node {
elemtype val;
node* lchild, * rchild;
}BiTreeNode, * BiTree;
/*构造二叉树*/
void CreatTree(BiTree &T, int n)
{
BiTree cur_node = NULL;
stack<BiTree> st; // 存节点的栈
int nn = n;
//n是用来判断结束的
int flag = 1;
while (!st.empty() || nn)//结束条件
{
string s;
cin >> s;//输入操作
if (s == "push")
{
BiTree tmp;
tmp = new (nothrow) BiTreeNode;//创建一个空间
if (tmp == NULL)
exit(-1);
elemtype ch;
cin >> ch;
tmp->val = ch;
tmp->lchild = NULL;
tmp->rchild = NULL;
if (nn == n)
{
T = tmp; //存根节点
st.push(T);
cur_node = st.top();
nn--;
continue;
}
// push前面还是push 说明这个T是st.top 的左孩子
if (flag == 1)
{
cur_node->lchild = tmp;
st.push(tmp);
}
// push前面还是push 说明这个T是st.top 的右孩子
if (flag == 0)
{
cur_node->rchild = tmp;
st.push(tmp);
flag = 1;
}
cur_node = st.top();
nn--;
}
if (s == "pop")
{
cur_node = st.top();
st.pop();
flag = 0;
}
}
}
/*后序遍历*/
void lastTraverse(BiTree T)
{
if (T == NULL)
return;
lastTraverse(T->lchild);
lastTraverse(T->rchild);
cout << T->val;
}
int main()
{
BiTree T = NULL ; //构建一棵二叉树 怎么联系起来呀? 根节点就是根节点
int n;
cin >> n;//输入n
CreatTree(T, n);
/*后序遍历*/
lastTraverse(T);
return 0;
}
我踩过的坑
我的总结和收获:
我的疑问:
- 什么是中序遍历?遍历的规律是什么?
- 二叉树是怎么构建出来的,是用BiTree *T, 还是 BiTree T?为什么T指向的一直是根节点?
- 解答: BiTree 也就是 BiTreeNode *, T一直是根节点是因为我们通过访问它的左右指针来达到效果
- 怎么做到传地址呢?对T进行修改呢?
- c++的&太好用啦!
- 能否通过push 和 pop的顺序来确定二叉树呢?比如 pop时设置为#, push 就把数据存起来?
- 我这个思路导致我一个星期都没有做出来
- stack的操作元素和先序存的数据非常非常类似,但是有个问题 ## 表示的是节点的孩子为空,如果是叶子节点,它的后面会有两个##, 但是pop掉的是叶子节点,而不是因为叶子节点的左孩子为空pop , 右孩子为空pop,stack操作下 push 和 pop的数是 一样的,但是##下#的个数一定是大于等于节点数的,故无法很好的确定每个空节点的位置
- n有什么用???
- 2 * n 行数据
- 我是结束用了
这道题目我花了非常非常久的时间,做出来的时候真的是热泪盈眶,那种感觉真的少有体会。
附上坎坷的过程
补充:
递归的后序遍历
/*后序遍历*/
void lastTraverse(BiTree T)
{
if (T == NULL)
return;
lastTraverse(T->lchild);
lastTraverse(T->rchild);
cout << T->val;
}