1 声明
数据结构和功能函数如前一篇博客所述,如有疑问,详见博客地址:http://blog.csdn.net/tubin100/article/category/6142259
2 后序
a 递归
void PostOrder(BiTree T) {
if (T) {
PostOrder(T->lChild);
PostOrder(T->rChild);
visit(T);
}
}
b 非递归
i)由于是后序遍历,必须先访问左右,再访问根,只有通过设置栈保存之前访问过的节点,这样才能在访问完右子树之后回到根节点。
ii)这样,就有两种情况访问到根节点。一种是第一次访问,另一种是已经访问过子树后回到根。 为了区分两种情况,设置一个指针用于标识访问过的节点。
void PostOrder_2(BiTree T) {
if (!T) {
Error();
return;
}
std::stack<BiTNode*> st;
BiTNode *p = T;
BiTNode *flag = nullptr; //标识被访问过的节点
while (p || !st.empty()) {
if (p) {
st.push(p);
p = p->lChild;
} else {
p = st.top(); //取当前栈顶
if (p->rChild && p->rChild != flag) {
p = p->rChild;
} else {
st.pop();
visit(p);
flag = p;
p = nullptr; //经过循环条件后,直接再取栈顶
}
}
}
}
3 应用
a 求给定某节点的所有祖先
i)思路分析:后序遍历中,访问到节点x时,当前栈中所有节点即为该节点的祖先。因此读取栈中的内容即可。
但因为STL栈只提供top()接口,不方便顺序访问。故此处,用vector模拟栈的实现。
ii)关键分析:一旦找到节点之后,打印栈中所有元素,直接返回。
void Ancestor(BiTree T, std::vector<BiTNode*>& v, BTElemType e) {
if (!T) {
Error();
return;
}
v.clear();
BiTNode *p = T;
BiTNode *pFlag = nullptr;
while (T || !v.empty()) {
if (p) {
if (p->data == e) {
//找到e时,该vector中所有元素即为e的ancestor
for (auto iter = v.begin(); iter != v.end(); ++iter) {
cout << (*iter)->data << " ";
}
return;
} else {
v.push_back(p); //模拟入栈
p = p->lChild;
}
} else {
p = *(v.end() - 1); //模拟取栈顶
if (p->rChild && p->rChild != pFlag) {
p = p->rChild;
} else {
v.pop_back(); //模拟出栈
pFlag = p;
p = nullptr;
}
}
}
}
b 求给定两个节点的最近公共祖先
i)思路分析:
先得到一个节点x的后序遍历栈st1,把该栈拷贝到另一个栈中st2,作为该节点的后序遍历栈
然后继续遍历,直到找到另一个元素y,此时st1栈中元素即为y的后序遍历栈
最后,寻找两个栈中最后一个相同的元素,即为两个元素的最近公共祖先
ii)缺陷:
该方法的主要缺陷是:x必须出现在y左边。
代码如下:
//寻找最近公共祖先主程序
BTElemType CommonAncestor(BiTree T, BTElemType x, BTElemType y) {
std::vector<BiTNode*> st1, st2;
BiTNode *p = T;
BiTNode *pFlag = nullptr;
BTElemType result = '?'; //标识是否有公共祖先
while (p || !st1.empty()) {
if (p) {
if (p->data == x) {
//b把当前所有元素拷贝到st2中
st2 = st1;
}
if (p->data == y){
result = FindLastCommonElem(st1, st2, T);
break;
}
st1.push_back(p);
p = p->lChild;
} else {
p = *(st1.end() - 1);
if (p->rChild && p->rChild != pFlag) {
p = p->rChild;
} else {
st1.pop_back();
pFlag = p;
p = nullptr;
}
}
}
return result; //调用者通过判断 result == '?'来得出是否存在公共组选
}
//当找到x和y之后,查找两个栈中最后一个相同元素
BTElemType FindLastCommonElem(std::vector<BiTNode*>& st1, std::vector<BiTNode*>& st2, BiTree T) {
if (st1.size() == 0 || st2.size() == 0) { //x和y中至少有一个为根节点
return T->data;
}
BiTNode *pResult = nullptr;
//笔者起初想的用最简单的办法直接扫描两个vector,然后找出最后一个公共元素,但时间复杂度是O(n^2),而此方法为O(n)
if (st1.size() < st2.size()) {
pResult = *(st1.end() - 1); //st1中最后一个元素即为公共元素
} else if (st1.size() > st2.size()){
pResult = *(st2.end() - 1); //st2中最后一个元素即为公共元素
} else {
auto iter1 = st1.begin();
auto iter2 = st2.begin();
for (; iter1 != st1.end(); ++iter1, ++iter2) {
if ((*iter1)->data != (*iter2)->data) {
break;
}
}
pResult = *(iter1 - 1);
}
return pResult->data;
}
4 总结
1 后序遍历的非递归代码给我的启示:在适当的时候做标记,
2 一旦掌握了后序遍历非递归,那么其的应用可以根据模版,适当的修改,即可解决问题。