Spider——遍历二叉树

本文详细介绍了二叉树的遍历方法,包括递归和非递归的前序、中序、后序遍历,以及从上到下的打印方式,如顺序、按层和之字形打印,同时涵盖垂序遍历。
摘要由CSDN通过智能技术生成

遍历二叉树

三种递归遍历

前序遍历

根——左——右
void preorder(TreeNode *root, vector<int> &path)
{
    if(root == nullptr) return;
    path.push_back(root->val);
    preorder(root->left, path);
    preorder(root->right, path);
}

中序遍历

左——根——右
void inorder(TreeNode *root, vector<int> &path)
{
	if(root == nullptr)	return;
	inroder(root->left,path);
	path.push_back(root->val);
	inorder(root->right,path);
}

后序遍历

左——右——根
void postorder(TreeNode *root, vector<int> &path)
{
	if(root == nullptr)	return;
	postorder(root->left,path);
	postorder(root->right,path);
	path.push_back(root->val);
}

三种非递归遍历

前序遍历

class Solution
{
public:
    vector<int> preorderTraversal(TreeNode* root)
    {
        vector<int> path;
        if(root == nullptr) return path;
       
        stack<TreeNode*> s;
        TreeNode *p = root;
        while(p || !s.empty())
        {
            if(p) //当左结点不为空时
            {
                path.push_back(p->val); //访问当前结点(父结点)
                s.push(p); //入栈
                p = p->left; //指向下一个左结点
            }
            else            //当左结点为空时
            {
                p = s.top();
                s.pop();         //出栈
 
                p = p->right;    //指向右结点
            }
        }
        return path;
    }
};

中序遍历

class Solution
{
public:
    vector<int> inorderTraversal(TreeNode* root)
    {
        vector<int> path;
        if(root == nullptr) return path;
       
        stack<TreeNode*> s;
        TreeNode *p = root;
        while(p || !s.empty())
        {
            if(p) //当左结点不为空时
            {
                s.push(p); //入栈
                p = p->left; //指向下一个左结点
            }
            else            //当左结点为空时
            {
                p = s.top();
                path.push_back(p->val); //访问栈顶元素(父结点)
                s.pop();         //出栈
                p = p->right;    //指向右结点
            }
        }
        return path;
    }
};

后序遍历

/**
 * 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:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        if(!root)   return res;
        stack<TreeNode*> s;
        s.push(root);
        while(!s.empty()){
            TreeNode* temp = s.top();
            s.pop();
            res.push_back(temp->val);
            if(temp->left) s.push(temp->left);
            if(temp->right)  s.push(temp->right);
        }
        reverse(res.begin(),res.end());		//将前序遍历翻转即可
        return res;
    }
};

//
vector<int> postorderTraversal(TreeNode* root)
{
    vector<int> result;
    stack<TreeNode*> s;
    if(root== nullptr) return result;
    
    TreeNode* p; //当前结点指针
    TreeNode* pre = nullptr; //用于记录上一次访问的结点
    s.push(root);            //根结点指针入栈
    while(!s.empty())//不为空时才会入栈,故p不可能为nullptr,无需像之前加p的判断
    {
        p = s.top();   // 指向栈顶元素
        bool temp1 = p->left == nullptr && p->right == nullptr; //如果当前结点为叶子结点
        bool temp2 = pre != nullptr && (pre == p->left || pre == p->right); //或者当前结点的左结点和右结点都已被访问过了(若pre=p->left说明右结点为空,因为栈中按照根右左这样的顺序入栈,根左这种结构才能出现这种情况)
        
        if(!temp1 && !temp2)//如果不是上面两种情况,直接入栈
        {
            //先将右结点入栈,再将左结点入栈,这样可以保证之后访问时先访问左结点在访问右结点
            if(p->right) s.push(p->right); //右结点入栈
            if(p->left) s.push(p->left);   //左结点入栈
        }
        else
        {
 
            result.push_back(p->val); //访问顺序:左、右、根
            s.pop();
            pre = p; //保存刚刚访问过的结点
        }
    } 
    return result;  
}

从上到下打印二叉树

1. 顺序打印

/**
 * 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:
    vector<int> levelOrder(TreeNode* root) {
        vector<int> result;
        if(root==NULL)  return result;
        queue<TreeNode*> dq;
        dq.push(root);
        while(!dq.empty()){
            TreeNode* temp=dq.front();	//当前根节点
            dq.pop();
            result.push_back(temp->val);	//加入对列
            
            //将当前结点的左右子结点入队
            if(temp->left)   dq.push(temp->left);
            if(temp->right)  dq.push(temp->right);

        }
        return result;
    }
};

2. 按层打印

a. 递归解法 O(n), O(1)

/**
 * 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:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> ans;
        pre(root,0,ans);
        return ans;
    }

    void pre(TreeNode* root,int depth,vector<vector<int>> &ans){
        if(!root) return;	//递归出口
        if(depth>=ans.size())   ans.push_back(vector<int> {}); //在下一层时,增加空容器(因为事先不知道树的层数,故要一边遍历,一边增加容器大小)
        ans[depth].push_back(root->val);	//将元素值push进第level层的容器(索引从0开始)
        pre(root->left,depth+1,ans);
        pre(root->right,depth+1,ans);
    }
};

b. 迭代解法 O(n),O(1)

/**
 * 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:
    vector<vector<int>> levelOrder(TreeNode* root) {
       vector<vector<int>> res;
       if(!root)    return res;
       queue<TreeNode*> q;
       q.push(root);
       while(!q.empty()){
           vector<int> tmp;      //用来保存每一层的节点
           int len=q.size();	//获取当前队列长度,即可确定当前层的节点个数
           for(int i=0;i<len;i++){	//遍历该层结点,并将下一层结点入队
               TreeNode* t=q.front();	//访问当前结点
               q.pop();	//出队
               tmp.push_back(t->val);
               
               //将当前结点的左右子结点入队
               if(t->left)  q.push(t->left); 
               if(t->right)  q.push(t->right);
          
           }
            res.push_back(tmp);
       }
       return res;
    }
};

3. 之字形打印

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 //分析:广度优先遍历(??感觉遍历顺序是先序遍历,为深度优先遍历),用一个bool记录是从左到右还是从右到左,每一层结束就翻转一下
//用level-order遍历,用奇数层偶数层判断,偶数层时反向存数
class Solution {
private:
    vector<vector<int>> res;
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        dfs(root,0);
        return res;
    }

    void dfs(TreeNode* root,int layer){
        if(root==NULL)  return;
        if(res.size()<layer+1)  res.push_back({});
        if(layer&1) res[layer].insert(res[layer].begin(),root->val);	//增加一个层数奇偶性的判断,奇数层反向输入
        else res[layer].push_back(root->val);
        
		//将当前结点的左右子结点入队
        dfs(root->left,layer+1);
        dfs(root->right,layer+1);
    }
};


/*
方法二:迭代法
按level order遍历,偶数层时翻转一下(可以用一bool型变量,每一层反号一次)
*/
class Solution
{
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode *root)
    {
        vector<vector<int> > res;
        if (root == nullptr) return res;
       
        queue<TreeNode*> q;
        q.push(root); //根结点入队
        bool right_to_left = false;
      
        while (!q.empty())
        {
            vector<int> level;
            int size = q.size(); //当前层的结点数
          
            for (int i = 0; i <  size; ++i) //遍历该层结点,并将下一层结点入队
            {
                TreeNode *node = q.front();
                level.push_back(node->val); //访问当前结点
                q.pop(); //出队
              
                //将当前结点的左右子结点入队
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
                //下一层的结点排在上一层结点之后
            }
          
            if(right_to_left) reverse(level.begin(), level.end()); //反序
            res.push_back(level);
            right_to_left = !right_to_left;
        }
        return res;
    }
};

垂序遍历

/* 掌握
问题:二叉树的垂直遍历
方法:层序遍历,并给每个结点赋上列号(对于每列元素而言,层序遍历访问的先后顺序满足垂直遍历规律)
把根节点给个序号0,然后开始层序遍历,
凡是左子节点则序号减1,右子节点序号加1,
这样我们可以通过序号来把相同列的节点值放到一起
*/
class Solution
{
public:
    vector<vector<int>> verticalOrder(TreeNode* root)
    {
        vector<vector<int>> res;
        if (!root) return res;
        
        map<int, vector<int>> m; //构建存储<序号,遍历序列>对的map
        queue<pair<int, TreeNode*>> q; //构建存储<序号,结点>对的队列
        
        q.push({0, root}); //根结点入队,根结点序号设为0
        while (!q.empty()) //层序遍历
        {
            auto a = q.front();
            m[a.first].push_back(a.second->val); //访问当前结点,将结点值push到相同列的容器中
            q.pop(); //出队
           
            
            //将下一层结点入队
            if (a.second->left) q.push( {a.first - 1, a.second->left} ); //左结点序号减一
            if (a.second->right) q.push( {a.first + 1, a.second->right} ); //右结点序号加一
            //下一层的结点排在上一层结点之后
        }
        
        for (auto mi : m)  //将map中遍历序列按顺序push到结果容器中(map内部会自动排序,序号从小到大排列遍历序列)
        {
            res.push_back(mi.second);
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值