二叉树:后序,递归和非递归,应用(求祖先问题)
来源:互联网 发布:手机传软件 编辑:程序博客网 时间: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
- 二叉树:后序,递归和非递归,应用(求祖先问题)
- 利用二叉树的非递归后序遍历求解最近公共祖先问题
- 二叉树遍历(递归和非递归)及应用
- 二叉树的前序、中序、后序的实现(递归和非递归)
- 二叉树非递归和递归遍历(先序,中序,后序)
- 数据结构:二叉树的前序,中序,后序遍历(递归和非递归)
- 二叉树的递归和非递归遍历(前序、中序、后序)
- 二叉树(3)----后序遍历,递归和非递归方式
- 二叉树的后序遍历(递归和非递归)
- 二叉树的遍历,递归和非递归,求深度
- 求二叉树深度 -- 递归和非递归实现
- 求二叉树深度的递归和非递归算法
- 二叉树递归、非递归求深度
- 二叉树(4)----求二叉树深度,递归和非递归
- 二叉树(11)----求二叉树的镜像,递归和非递归方式
- 二叉树(7)----求二叉树叶子节点个数,递归和非递归
- 后序遍历二叉树非递归
- 非递归后序遍历二叉树
- android studio gradle 空项目
- ERROR 1840 (HY000) at line 24: GTID_PURGED can only be set when GTID_EXECUTED is empty.
- 第三周项目(4):百鸡问题
- JavaScript 事件模型
- 第二章:HTML常用标签
- 二叉树:后序,递归和非递归,应用(求祖先问题)
- andorid与ros互操作的关键点
- Xcode git的使用
- mysql不支持中文字符的解决办法
- Editable和string区别
- hihocoder #1273 : 清理海报
- 如何正确理解BitCode
- linux的文件结构
- POJ 2488 A Knight's Journey【dfs】