有关树的算法可以从最基础的遍历(前中后层)、遍历延伸(公共祖先、对称树)、集合思想(并查集找直系亲属)。
1.遍历
遍历就是按照一定的顺序访问树的节点,由于树是非线性结构,遍历树其实就是将非线性结构转换为线性序列展示。
前、中、后遍历常有的是递归遍历和非递归遍历。
1.1递归访问的模式如下:
<span style="font-size:18px;">void Order(TreeNode *root)
{
if(root != NULL)
{ //preVisit(T); //前序访问根节点
Order(T->lchild); //访问左子结点
//inVisit(T); //中序访问根节点
Order(T->rchild); //访问右子结点
//postVisit(T); //后序访问根节点
}
} </span>
1.2非递归遍历
非递归算法主要是利用循环+栈数据结构实现的。模式如下
1.2.1先序或中序遍历(非递归)
思路:访问T->data后,将T入栈,(先序遍历左子树);遍历完左子树返回时,栈顶元素应为T,(中序遍历左子树),出栈,再遍历T的右子树。
<span style="font-size:18px;">void Order2(TreeNode* root)
{
stack<TreeNode*> stack;
TreeNode*p = root;//p是遍历指针
while(p || !stack.empty())//栈不空或者p不空时循环
<span style="white-space:pre"> </span>{
if(p != NULL)
<span style="white-space:pre"> </span>{
stack.push(p);//存入栈中
//preVisit(p);//前序遍历访问根节点
p = p->lchild;//遍历左子树
}
else
<span style="white-space:pre"> </span>{
p = stack.top();//退栈
//inVisit(p);//中序遍历访问根节点
stack.pop();
p = p->rchild;//遍历右子树
}
<span style="white-space:pre"> </span>}
}</span>
1.2.2后序遍历.
有一篇经典的总结非递归遍历二叉树的方法 http://www.jianshu.com/p/49c8cfd07410
总结出来的模式为
<span style="font-size:18px;">void order(TreeNode *root, vector<int> &path)
{
stack< pair<TreeNode *, bool> > s;
s.push(make_pair(root, false));
bool visited;
while(!s.empty())
{
root = s.top().first;
visited = s.top().second;
s.pop();
if(root == NULL)
continue;
if(visited)
{
path.push_back(root->val);//访问根节点
}
else
{
//s.push(make_pair(root, true));//后序遍历,访问根节点<span style="font-family: Arial, Helvetica, sans-serif;">权限开通</span>
s.push(make_pair(root->right, false));
//s.push(make_pair(root, true));//中序遍历,访问根节点权限开通
s.push(make_pair(root->left, false));
//s.push(make_pair(root, true));//前序遍历,访问根节点权限开通
}
}
}</span>
1.2.3层序遍历(队列)
层序遍历相对简单,采用队列思维,每层压队列,直到队列为空即可结束。
</pre><p><span style="font-size:18px;"></span></p><h2><span style="font-size:18px;">1.3公共祖先问题</span></h2><div><span style="font-size:18px;">求解公共祖先问题主要有:<span style="font-family: verdana, Arial, Helvetica, sans-serif; line-height: 25.2px;">在线法和离线法.</span></span><p style="margin: 10px auto; font-family: verdana, Arial, Helvetica, sans-serif; line-height: 25.2px;"><span style="font-size:18px;">在线法:指每提出一次请求,便给出一次应答</span></p><p style="margin: 10px auto; font-family: verdana, Arial, Helvetica, sans-serif; line-height: 25.2px;"><span style="font-size:18px;">离线法:收集所有的请求,然后统一进行处理</span></p></div><p><span style="font-size:18px;">1.3.1采用递归方法求公共祖先</span><pre name="code" class="cpp"><span style="font-size:18px;">/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution
{
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(root == NULL || root == p || root == q) return root;
TreeNode *ltree = lowestCommonAncestor(root->left,p,q);
TreeNode *rtree = lowestCommonAncestor(root->right,p,q);
if(ltree && rtree) return root;
return ltree?ltree:rtree;
}
};</span>
1.3.2Tarjan算法(离线法dfs+并查集)
算法的基本思想为:任选一结点开始进行深度优先搜索dfs(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。
推荐一篇文章:http://blog.csdn.net/v_july_v/article/details/18312089
1.3.3RMQ算法(在线法dfs+RMQ)
RMQ,全称为Range Minimum Query,顾名思义,则是区间最值查询,它被用来在数组中查找两个指定索引中最小值的位置。
推荐一篇文章:http://www.cnblogs.com/qianye/archive/2012/12/03/2800384.html