zoukankan      html  css  js  c++  java
  • 二叉树的遍历:先序,中序,后序,递归,非递归,层次遍历

    二叉树遍历的递归写法:

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <stack>
    using namespace std;
    
    struct TreeNode {
        int val;
        TreeNode *left;
        TreeNode *right;
        TreeNode(int x):val(x), left(NULL), right(NULL) {}
    };
    
    // 先序遍历
    void preOrder(TreeNode* root)
    {
        if(root == NULL)
            return;
        cout << root->val << " ";
        preOrder(root->left);
        preOrder(root->right);
    }
    
    // 中序遍历
    void inOrder(TreeNode* root)
    {
        if(root == NULL)
            return;
        inOrder(root->left);
        cout << root->val << " ";
        inOrder(root->right);
    }
    
    // 后序遍历
    void lastOrder(TreeNode* root)
    {
        if(root == NULL)
            return;
        lastOrder(root->left);
        lastOrder(root->right);
        cout << root->val << " ";
    }
    
    



    ## 非递归写法:
    - 先序遍历

    题目链接

    vector<int> preorderTraversal(TreeNode* root) {
            vector<int> res;
            stack<TreeNode*> temp;
            if(root == NULL)
                return res;
            temp.push(root);
            while(!temp.empty())
            {
                TreeNode* pos = temp.top();
                temp.pop();
                res.push_back(pos->val);
                if(pos->right != NULL)
                    temp.push(pos->right);
                if(pos->left != NULL)
                    temp.push(pos->left);
            }
            return res;
    }
    
    


    - 中序遍历

    题目链接

    // 只有当左子树已经访问完后,才能访问根节点
    /*
        对于任一节点 P
        1)  若其左孩子不为空,则将 P 入栈并将 P 的左孩子置为当前 P ,
            然后对当前 P 再进行相同的处理;
        2)  若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶节点,
            然后将当前 P 置为栈顶节点的右孩子;
        3)  直到 P 为 NULL 并且栈为空则遍历结束;
    */
    vector<int> inOrderTraversal(TreeNode* root){
        vector<int> res;
        stack<TreeNode*> temp;
        if(root == NULL)
            return res;
        TreeNode* pos = root;
        while(!temp.empty() || pos != NULL)
        {
            if(pos != NULL){
                // push 左子树入栈
                temp.push(pos);
                pos = pos->left;
            }
            else
            {
                pos = temp.top();
                temp.pop();
                res.push_back(pos->val);
                pos = pos->right;
            }
        }
        return res;
    }
    
    


    - 后序遍历

    题目链接

    // 先入栈根,然后是右子树,最后左子树
    // 要求最后访问根节点, 即访问该根节点时必须访问完左子树和右子树,
    // 我们只需要保证访问某一节点时,该节点的右子树已经被访问, 否则需要
    // 将该节点重新压入栈。
    /*
    对于任一结点 P, 将其入栈, 然后沿其左子树一直往下搜索, 直到搜索到没有左孩子的结点,
    此时该结点出现在栈顶,但是此时不能将其出栈并访问,因为其右孩子还没有被访问。
    所以接下来按照相同的规则对其右子树进行相同的处理,当访问完其右孩子时,该结点又出现在
    栈顶,此时可以将其出栈并访问。 这样就保证了正确的访问顺序。 可以看出,在这个过程中,
    每个结点都两次出现在栈顶,并且只有第二次出现在栈顶时,才能访问它。
    */
     vector<int> postorderTraversal(TreeNode* root) {
            vector<int> res;
            stack<TreeNode*> tmp;
            if(root == NULL)
                return res;
            TreeNode* pos = root;   // 记录正在访问的结点
            TreeNode* pre = NULL;   // 记录前一个访问的结点
            do
            {
                while(pos != NULL)      // 把左子树结点都压进栈
                {
                    tmp.push(pos);
                    pos = pos->left;
                }
                pre = NULL;             // 左结点压完后,初始化前一个访问结点为 NULL
                while(!tmp.empty())
                {
                    pos = tmp.top();
                    tmp.pop();
                    if(pos->right == pre)       // 右孩子已经被访问
                    {
                        res.push_back(pos->val);    // 右孩子已经被访问,可以访问该结点
                        pre = pos;      // 记录刚刚访问的结点
                    }
                    else
                    {
                        tmp.push(pos);  // 第一次访问该结点,再次放入栈中
                        pos = pos->right;
                        break;
                    }
                }
            } while(!tmp.empty());
    
            return res;
        }
    



    - 层次遍历

    题目链接

    /*
    struct TreeNode {
    	int val;
    	struct TreeNode *left;
    	struct TreeNode *right;
    	TreeNode(int x) :
    			val(x), left(NULL), right(NULL) {
    	}
    };*/
    class Solution {
    public:
        vector<int> PrintFromTopToBottom(TreeNode* root) {
    		vector<int> res; 
            queue<TreeNode*> tmp; 
            if(root == NULL)
                return res; 
            TreeNode* pos = root; 
            tmp.push(pos); 
            while(!tmp.empty())
           	{
                pos = tmp.front(); 
                tmp.pop();
                res.push_back(pos->val); 
                if(pos->left != NULL)
                    tmp.push(pos->left); 
                if(pos->right != NULL)
                    tmp.push(pos->right); 
            }
            return res; 
        }
    };
    

    [题目链接](https://leetcode.com/problems/binary-tree-level-order-traversal/#/description)
    方法一:(思路) 在普通层次遍历的基础上,用 NULL 分隔各层。 
    
    /**
     * 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;
            vector<int> level; 
            queue<TreeNode*> tmp; 
            if(root == NULL)
                return res; 
            TreeNode* pos = root;
            tmp.push(pos); 
            tmp.push(NULL);         // 以 NULL 作为每层之间的分隔板 
            while(!tmp.empty())
            {
                pos = tmp.front(); 
                tmp.pop(); 
                if(pos == NULL)
                {
                    res.push_back(level);  
                    level.resize(0); 
                    if(tmp.size() > 0)      // 注意当队列为空时不能再插入 NULL, 不然会无限循环。
                        tmp.push(NULL);     // 当前层的所有子节点(下一层)都已加入队列,插入 NULL 进行分隔       
                }
                else
                {
                    level.push_back(pos->val); 
                    if(pos->left != NULL)
                        tmp.push(pos->left); 
                    if(pos->right != NULL)
                        tmp.push(pos->right); 
                }
            }
            return res; 
        }
    };
    

    方法二:(思路) 先序遍历, 全局数组记录各层结点的值。 
    
    /**
     * 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) {
            if(root == NULL)
                return res; 
            buildLevels(root, 0); 
            return res; 
        }
        
    private:
    vector<vector<int> > res;     
        void buildLevels(TreeNode* pos, int depth)    // 先序遍历
        {
            if(pos == NULL)
                return; 
            if(res.size() == depth)
                res.push_back(vector<int>());   // 每到一个新层,初始化这一层
            res[depth].push_back(pos->val);     
            buildLevels(pos->left, depth+1); 
            buildLevels(pos->right, depth+1);
        }
    };
    

    方法三: 上层完全出队后(此时下层完全入队),记录一下队列的大小。 ```cpp /** * 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> levelOrder(TreeNode* root) { vector > res; if(root == NULL) return res;
        TreeNode* pos = root; 
        queue<TreeNode*> que;
        que.push(pos); 
        
        while(!que.empty())
        {
            int sz = que.size();
            vector<int> temp; 
            while(sz--)
            {
                pos = que.front();
                que.pop();
                temp.push_back(pos->val);
                if(pos->left != NULL)
                    que.push(pos->left);
                if(pos->right != NULL)
                    que.push(pos->right);
            }
            
            res.push_back(temp);
        }
        
        return res; 
    }
    

    };

    <br/>
    <br/>
    
    - 小进阶: 之字型打印二叉树
    [题目连接](https://www.nowcoder.com/practice/91b69814117f4e8097390d107d2efbe0?tpId=13&tqId=11212&tPage=3&rp=3&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking)
    
    // 方法一: 使用 reverse()
    ```cpp
    /*
    struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
        TreeNode(int x) :
                val(x), left(NULL), right(NULL) {
        }
    };
    */
    class Solution {
    public:
            vector<vector<int> > Print(TreeNode* pRoot) {
            	vector<vector<int> > res; 
                if(pRoot == NULL)
                    return res; 
                
                vector<int> level; 
                TreeNode* pos = pRoot; 
                queue<TreeNode*> tmp;
                
                tmp.push(pos);
                tmp.push(NULL);		// 以 NULL 作为隔板
                
                bool flag = true; 
                while (!tmp.empty())
                {
                    pos = tmp.front(); 
                    tmp.pop();
                    
                    if(pos == NULL)
                    {
                        if(flag)
                        	res.push_back(level);
                        else
                        {
                            reverse(level.begin(), level.end());
                            res.push_back(level); 
                        }
                            
                        
                        level.resize(0);
                        if(tmp.size() > 0)	// 注意: 当 tmp为空时,不要再插入 NULL. 不然无限循环
                            tmp.push(NULL);
                        flag = !flag;
                    }
                    else
                    {
                        level.push_back(pos->val);
                        if(pos->left != NULL)
                            tmp.push(pos->left);
                        if(pos->right != NULL)
                            tmp.push(pos->right);
                    }
                }
                
                return res; 
            }
        
    };
    

    // 方法二: 祭出利器 deque

    /*
    struct TreeNode {
        int val;
        struct TreeNode *left;
        struct TreeNode *right;
        TreeNode(int x) :
                val(x), left(NULL), right(NULL) {
        }
    };
    */
    class Solution {
    public:
        vector<vector<int> > Print(TreeNode* pRoot) {
            vector<vector<int> > res; 
            if(pRoot == NULL)
                return res; 
            
            deque<TreeNode*> deq;
            TreeNode* pos = pRoot; 
            deq.push_back(pos);
            bool flag = true; 
            while(!deq.empty())
            {
                flag = !flag; 
                int sz = deq.size();
                vector<int> temp; 
                while(sz--)
                {
                    if(flag)	// pop_front, push_back, right then left
                    {
                        pos = deq.front();
                        deq.pop_front();
                        temp.push_back(pos->val);
                        if(pos->right != NULL)
                            deq.push_back(pos->right);
                        if(pos->left != NULL)
                            deq.push_back(pos->left); 
                    }
                    else	// pop_back, push_front, left then right
                    {
                        pos = deq.back();
                        deq.pop_back();
                        temp.push_back(pos->val);
                        if(pos->left != NULL)
                            deq.push_front(pos->left);
                        if(pos->right != NULL)
                            deq.push_front(pos->right);
                    }
                }
                res.push_back(temp);
            }
            
            return res; 
        }
        
    };
    


  • 相关阅读:
    linux常用的一些访问目录
    专业术语
    java基础知识回顾之javaIO类--File类应用:获取指定目录下面的指定扩展名的文件,将文件的绝对路径写入到目的文件当中
    java基础知识回顾之javaIO类--File类应用:删除带内容的目录
    java基础知识回顾之javaIO类--File类应用:过滤器接口FilenameFilter和FileFilter
    java基础知识回顾之javaIO类--File类应用:递归深度遍历文件
    java基础知识回顾之javaIO类--File类
    java基础知识回顾之java集合类-Properties集合
    java基础知识回顾之---java StringBuffer,Stringbuilder与String的区别
    java基础知识回顾之---java StringBuilder与StringBuffer异同点
  • 原文地址:https://www.cnblogs.com/acm1314/p/6758610.html
Copyright © 2011-2022 走看看