二叉树三种遍历(迭代、递归)文章目录
1.迭代版本
1)先序遍历
方法一(较简单)
方法二(通用法)
方法二修改版
2)中序遍历
方法一
先序中序对比
方法二
3)后序遍历
方法一
方法二(辅助标记)
方法二
3. 递归版本
1.迭代版本
1)先序遍历
方法一(较简单)
访问栈顶节点,并将右子树、左子树按顺序入栈(先右再左,左子树比右子树先出栈)。左子树访问完才会访问右子树,各节点按照访问顺序分别入栈,转到第一步。
PS:这种方法是由递归版本消除尾递归得到,不具有一般性。
简化:
while(栈非空){
栈顶出栈、访问->右子树进栈->左子树进栈
}
【TIP】空节点入栈和不入栈分别有一种写法
//左右子树都入栈,空节点不入栈
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> Stack;
Stack.push(root);
while(!Stack.empty())
{
TreeNode* curNode=Stack.top();
res.push_back(curNode->val);
Stack.pop();
if(curNode->right)
Stack.push(curNode->right);
if(curNode->left)
Stack.push(curNode->left);
}
return res;
}
};
//另一种写法,空节点入栈,可以处理空节点
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> Stack;
Stack.push(root);
while(!Stack.empty())
{
TreeNode* curNode=Stack.top();
res.push_back(curNode->val);
Stack.pop();
if(curNode==NULL)
continue;
Stack.push(curNode->right);
Stack.push(curNode->left);
}
return res;
}
};
方法二(通用法)
内循环:访问当前节点并将右子树入栈,往左子树方向一直访问下去。左子树访问到底退出循环,进入外循环。
外循环:将栈顶右子树取出,转到上一步。
因为左链已经访问完毕,所以只需将右子树入栈。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> S;
vector<int> v;
TreeNode* rt = root;
while(rt || S.size()){
while(rt){
S.push(rt->right); //NULL有可能入栈,但并不影响
v.push_back(rt->val);
rt=rt->left;
}
rt=S.top();
S.pop();
}
return v;
}
}
方法二修改版
用左链代替右子树入栈,出栈时,用节点右子树代替出栈节点。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> S;
vector<int> v;
TreeNode* rt = root;
while(rt || S.size()){
while(rt){
S.push(rt);
v.push_back(rt->val);
rt=rt->left;
}
rt=S.top();
S.pop();
rt=rt->right;//rt可能为NULL,但并不影响
}
return v;
}
**
2)中序遍历 方法一
**
因为中序遍历得先访问左节点,所以沿左侧分支寻找最左节点时并没有访问根节点。因此,需要将根节点入栈(后进先出),出栈时同时访问右节点(将右节点设为根节点,完成此子任务)。
PS:由先序遍历方法二第二个版本推广得到,只有几行有变化。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> S;
vector<int> v;
TreeNode* rt = root;
while(rt || S.size()){
while(rt){
S.push(rt);
rt=rt->left;
}
rt=S.top();
S.pop();
v.push_back(rt->val);
rt=rt->right; //转向右子树(可能为空,但不影响)
}
return v;
}
)
先序中序对比`
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> S;
vector<int> v;
TreeNode* rt = root;
while(rt || S.size()){
while(rt){
S.push(rt);
//先序遍历:v.push_back(rt->val);
rt=rt->left;
}
rt=S.top();
S.pop();
//中序遍历:v.push_back(rt->val);
rt=rt->right; //转向右子树(可能为空,但不影响)
}
return v;
}
)
方法二
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> s1;
vector<int> res;
if(root!=NULL){
while(root!=NULL || !s1.empty()){
if(root!=NULL){
s1.push(root);
root=root->left;
}
else{
root=s1.top();
res.push_back(root->val);
s1.pop();
root=root->right;
}
}
}
return res;
}
};
3)后序遍历 方法一
将前序遍历的中左右变为中右左,然后再将中右左反向!
反向即为先进后出,用第二个栈实现!!
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> s1;
stack<TreeNode*> s2;
vector<int> res;
if(!root) return res;
s1.push(root);
while(!s1.empty()){
root=s1.top();
s1.pop();
s2.push(root);
if(root->left)
s1.push(root->left);
if(root->right)
s1.push(root->right);
}
while(!s2.empty()){
res.push_back(s2.top()->val);
s2.pop();
}
return res;
}
};
方法二(辅助标记)
由中序遍历推广得到,前半部分几乎一样。
不同点在于:沿左链入栈到左链最低点后,并没有直接弹出栈顶,而是
先判断:
该节点是否有右节点 或者 右节点是否访问完毕
若右节点访问完毕 或者 无右节点,才开始访问操作:
弹出并访问该节点,并把该节点赋值给pre(辅助标记,表示前一个被访问的节点),令节点为空,开始对下一个栈顶的判断。
若右节点没有被访问(pre不是右节点):将右节点独立为子任务,遍历该子任务。
class Solution {
public:
stack<TreeNode*> mystack;
vector<int> ans;
TreeNode* curr = root;
TreeNode* pre = NULL;//辅助标记:前一个被访问的节点
while(curr || !mystack.empty())
{
while(curr)
{
mystack.push(curr);
curr = curr->left;
}
curr = mystack.top();
//若前一个访问的是右节点(已访问过)或者没有右节点,则访问该节点
if(!curr->right || pre == curr->right){
mystack.pop();
ans.push_back(curr->val);
pre = curr;//将该节点设为前一个访问的节点
curr = NULL;
}else{//右节点还没有被访问,则将右节点独立为子任务进行遍历
curr = curr->right;
pre = NULL;
}
}
return ans;
}
方法二
将前序遍历中的left、right相互替换,结果再逆序输出,即为后序遍历结果。但本质上并不是后序遍历。类似的方法还有很多。
3. 递归版本
根据遍历顺序,分别把遍历函数放在左右子树递归语句前中后即可
//前序:
class Solution {
public:
vector<int> vec;
vector<int> preorderTraversal(TreeNode* root) {
if(root==NULL) return vec;
vec.push_back(root->val);
if(root->left) preorderTraversal(root->left);
if(root->right) preorderTraversal(root->right);
return vec;
}
};
//中序:
class Solution {
public:
vector<int> vec;
vector<int> preorderTraversal(TreeNode* root) {
if(root==NULL) return vec;
if(root->left) preorderTraversal(root->left);
vec.push_back(root->val);
if(root->right) preorderTraversal(root->right);
return vec;
}
};
//后序:
class Solution {
public:
vector<int> vec;
vector<int> preorderTraversal(TreeNode* root) {
if(root==NULL) return vec;
if(root->left) preorderTraversal(root->left);
if(root->right) preorderTraversal(root->right);
vec.push_back(root->val);
return vec;
}
};