一、先序遍历:
1.根节点进栈
2.节点出栈,并访问,若有右孩子,右孩子进栈,若有左孩子,左孩子进栈
3.重复2直至栈空
--------------01
------02-------------09
--03-----06-----10-----13
04-05-07-08-11-12-14-15
/**
* Definition for binary tree
* 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> result;
if(!root)return result;
stack<TreeNode *> s;
s.push(root);
while(!s.empty()){
TreeNode *p = s.top();
s.pop();
result.push_back(p->val);
if(p->right) s.push(p->right);
if(p->left) s.push(p->left);
}
return result;
}
};
二、中序遍历:
1.节点进栈,若有左孩子则将指针指向左孩子,重复进行,直到最左
2.节点退栈,并访问。若有右孩子,则将指针指向右孩子
3.重复1 2直至栈空且树空
--------------08
------04-------------12
--02-----06-----10-----14
01-03-05-07-09-11-13-15
class Solution {
public:
vector<int> inorderTraversal(TreeNode *root) {
vector<int> result;
if(!root) return result;
TreeNode *p = root;
stack<TreeNode *> s;
while(!s.empty() || p != nullptr){
if(p != nullptr){//向左一直到最左
s.push(p);
p = p->left;
}else{
//访问节点,再转向右子树
p = s.top();
s.pop();
result.push_back(p->val);
//转向右子树
if(p->right){
p = p->right;
}else{
p = nullptr;
}
}
}
return result;
}
};
三、后序遍历:
1.节点进栈,若有左孩子则将指针指向左孩子,重复进行,直到最左
2.取栈顶节点(不出栈),若无右孩子或右孩子已经访问,则节点出栈并访问,并作访问标记。若有右孩子且右孩子未被访问,则将指针指向右孩子
3.重复1 2直至栈空且树空
--------------15
------07-------------14
--03-----06-----10-----13
01-02-04-05-08-09-11-12
class Solution {
public:
vector<int> postorderTraversal(TreeNode *root) {
vector<int> result;
if(!root) return result;
TreeNode *p = root;
//后序遍历需要一个指针来标记前一个访问的节点
//用于区分是从左子树还是右子树回退到根节点
TreeNode *pre = nullptr;
stack<TreeNode *> s;
while(!s.empty() || p != nullptr){
if(p != nullptr){//向左一直到最左
s.push(p);
p = p->left;
}else{
//访问节点之前,先要判断右子树是否访问过
p = s.top();
if(p->right && pre != p->right){
//右子树未访问,转向右子树
p = p->right;
}else{
//右子树不存在或者已经访问过,则访问根节点
s.pop();
result.push_back(p->val);
pre = p;
p = nullptr;
}
}
}
return result;
}
};
四、层次遍历:
1.根节点入队
2.队首节点出队并访问,若有左孩子,则左孩子入队,若有右孩子,则右孩子入队
3.重复2直至队空
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
vector<vector<int> >result;
if(!pRoot)return result;
queue<TreeNode *> que;
que.push(pRoot);
TreeNode *p;
size_t levSize = que.size();//记录每层的节点数量
vector<int> levArr;//保存每一层从左至右的数据
while(!que.empty()){
p = que.front();
if(p->left) que.push(p->left);
if(p->right) que.push(p->right);
levArr.push_back(p->val);
que.pop();
--levSize;
if(levSize == 0){//一层遍历完
result.push_back(levArr);
levArr.clear();
levSize = que.size();
}
}
return result;
}
};
总结:
中序遍历和后序遍历有着相似的代码结构,在访问节点的时候,左子树都已经访问完了。中序遍历是直接访问节点,再转向右子树。后序遍历是先要判断是右子树是否存在访问过,如果存在且未访问过,须先访问右子树,待右子树访问完毕后再访问根节点。
后序遍历非递归是先访问左子树、再右子树、最后根。可以用栈来存储节点,必须分清返回根节点时,是从左子树返回的,还是从右子树返回的,所以,使用辅助指针pre,指向最近访问过的节点。也可以在节点中增加一个标志域,记录是否已经被访问
后序遍历非递归法可以用来求根节点到某节点的路径、公共祖先等。
因为访问某个节点p的时候,栈中节点恰好是p节点的所以祖先,从栈底到栈顶再加上p节点,刚好构成根节点到p节点的一条路径。
如求pq公共祖先的思路:后序找到p后q,保存此时的栈中元素。在继续找到另一个,用此时的栈中元素和先前元素中的栈去比对。