二叉树:后序,递归和非递归,应用(求祖先问题)

来源:互联网 发布:手机传软件 编辑:程序博客网 时间:2024/06/05 12:01

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  一旦掌握了后序遍历非递归,那么其的应用可以根据模版,适当的修改,即可解决问题。





0 0