很久没看数据结构,许多东西生疏了。这两天在leetcode上做了几道关于二叉树遍历的题目,前序和中序相对容易,但后序遍历写了好久,记录在此,以便下次回读。
题目链接:http://oj.leetcode.com/problems/binary-tree-preorder-traversal/
http://oj.leetcode.com/problems/binary-tree-inorder-traversal/
http://oj.leetcode.com/problems/binary-tree-postorder-traversal/
关于三种遍历的代码形式很多,基本思想差不多,这里主要记录用栈来解决。
树的结构定义:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
三种遍历:
class Solution {
public:
vector<int> preorderTraversal(TreeNode *root) {
vector<int> res;
stack<const TreeNode *> tn_sta;
const TreeNode *p=root;
while(p || !tn_sta.empty()){
if(p){ //当前结点非空
res.push_back(p->val);
tn_sta.push(p);
p=p->left;
}
else{ //当前结点为空,但栈非空
p=tn_sta.top();
tn_sta.pop();
p=p->right;
}
}
return res;
}
vector<int> inorderTraversal(TreeNode *root) {
vector<int> res;
vector<const TreeNode *> sta;//vector模拟栈
const TreeNode *p=root;
while(p || !sta.empty()){
if(p){ //当前结点非空
sta.push_back(p);
p=p->left;
}
else{ //当前结点为空,但栈非空
p=sta.back();
sta.pop_back();
res.push_back(p->val);
p=p->right;
}
}
return res;
}
vector<int> postorderTraversal(TreeNode *root) {
vector<int> res;
vector<TreeNode*> sta;
TreeNode *p=root,*q=NULL;//q记录根节点是否访问过
while(p || !sta.empty()) {
if(p){
sta.push_back(p);
p=p->left;
}else{
p=sta.back();
if(p->right==q){//右子树为空,或已访问过
sta.pop_back();//根节点出栈
res.push_back(p->val);//根节点值记录到vector中
q=p;//q回升到p
p=NULL;//p置空,防止死循环
}else{ //右子树未访问
q=p->right;//q下降到p的右结点
p=p->right;//p进入右子树访问
}
}
}
return res;
}
};
这里主要分析下后序遍历:
栈中的结点要在访问完其右子树后才能出栈,所以需要标记其右子树是否已经访问,最直接的想法是在数的结点定义中加一个变量标记其右子树是否访问,但这题中的树的结构已经给定,无法在其中添加新的成员变量。这里采用一个指针q来记录,q初值为空,若右子树未访问q随遍历指针p进入右子树,若右子树已访问或为空,则q退回到右子树的根结点。当根节点出栈后,不能忘记将p置空,否则死循环。