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

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





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值