二叉树的遍历递归与迭代实现

二叉树的遍历

二叉树主要有两种遍历方式:

深度优先遍历:先往深走,遇到叶子节点再往回走。
广度优先遍历:一层一层的去遍历。

那么从深度优先遍历和广度优先遍历进一步拓展,才有如下遍历方式:

深度优先遍历
前序遍历(递归法,迭代法)
中序遍历(递归法,迭代法)
后序遍历(递归法,迭代法)

广度优先遍历
层次遍历(迭代法)

经常会使用递归的方式来实现深度优先遍历,也就是实现前中后序遍历;
迭代:前中后序遍历的逻辑其实都是可以借助栈使用非递归的方式来实现的。

广度优先遍历的实现一般使用队列来实现,这也是队列先进先出的特点所决定的,因为需要先进先出的结构,才能一层一层的来遍历二叉树

1. 二叉树的定义

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

2. 二叉树遍历的递归实现

递归算法的三要素:

1、「确定递归函数的参数和返回值:」确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。

2、「确定终止条件:」写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。

3、「确定单层递归的逻辑:」确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。

2.1 前序遍历

class Solution {
public:
    void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        vec.push_back(cur->val);    // 中
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> result;
        traversal(root, result);
        return result;
    }
};

2.2 中序遍历

void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        vec.push_back(cur->val);    // 中
        traversal(cur->right, vec); // 右
    }

2.3 后序遍历

  void traversal(TreeNode* cur, vector<int>& vec) {
        if (cur == NULL) return;
        traversal(cur->left, vec);  // 左
        traversal(cur->right, vec); // 右
        vec.push_back(cur->val);    // 中
    }

3.二叉树遍历的迭代实现

3.1 前序遍历

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int>res;
        //特例
        if(root==nullptr) return res;
        stack<TreeNode*>sta;
        sta.push(root);
        while(!sta.empty()){
            TreeNode *cur=sta.top();
            sta.pop();
            res.push_back(cur->val);//中
            //因为栈是先进后出,所以先将右子树压入栈中;
            if(cur->right) sta.push(cur-> right);
            if(cur->left) sta.push(cur->left);
        }
        return res;
    }
};

3.2 中序遍历

刚在迭代的过程中,其实我们有两个操作:

1、处理:将元素放进result数组中
2、访问:遍历节点

分析一下为什么刚刚写的前序遍历的代码,不能和中序遍历通用呢,因为前序遍历的顺序是中左右,先访问的元素是中间节点,要处理的元素也是中间节点,所以刚刚才能写出相对简洁的代码,因为要访问的元素和要处理的元素顺序是一致的,都是中间节点

中序遍历,中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进result数组中),这就造成了处理顺序和访问顺序是不一致的

在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int>res;
        if(root==nullptr) return res;
        stack<TreeNode*> sta;
        TreeNode *cur=root;
        while(cur!=nullptr || !sta.empty() ){
            if(cur!=nullptr){ //指针用来访问结点,访问到最低层
                sta.push(cur);  //将访问的节点放进栈
                cur=cur->left;  //左
            }else{
                cur=sta.top();  //从栈里弹出数据,放进res中
                sta.pop();
                res.push_back(cur->val); //中
                cur=cur->right;  //右
                //这里不能写为if(cur->right) cur=cur->right; 因为当前节点可能存在左子树
            }
        }
        return res;
    }
};

3.3 后序遍历

前序遍历为中左右,中序遍历为左中右,后序遍历为左右中

后序遍历为左右中,先序遍历为中左右,需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int>res;
        if(root==nullptr) return res;
        stack<TreeNode *>sta;
        sta.push(root);
        while(!sta.empty()){
            TreeNode *cur=sta.top();
            sta.pop();
            if(cur==nullptr)  continue;
            res.push_back(cur->val);
            sta.push(cur->left);//对比于前序遍历,更改一下的入栈顺序
            sta.push(cur->right);
        }
        reverse(res.begin(),res.end()); //将结果反转之后变为左中右的顺序。
        return res;
        
    }
};

4.二叉树的层序遍历

leetcode 102、107、199、635、429、515都可以用层序遍历解决

解题思路:层次遍历

  1. 初始化:一个队列queue q, 将root节点入队列q
  2. 如果队列不空,做如下操作:
  3. 弹出队列头,保存为node,将node的左右非空孩子加入队列
  4. 做2,3步骤,直到队列为空

如果不需要确定当前遍历到了哪一层,模板如下:

void bfs() {
    vis[] = 0;//标记数组
    queue<int> pq(start_val);

    while (!pq.empty()) {
        int cur = pq.front(); 
        pq.pop();
        for (遍历cur所有的相邻节点nex) {
            if (nex节点有效 && vis[nex]==0){
                vis[nex] = 1;//节点以访问
                pq.push(nex)
            }
        }
    }
}

如果需要确定遍历到哪一层,模板如下:

void bfs() {
    int level = 0;//表示遍历的层数
    vis[] = 0; // or set
    queue<int> pq(original_val);
    while (!pq.empty()) {
    //这里一定要使用固定大小size,定义一个变量,不要使用que.size(),因为que.size是不断变化的
        int sz = pq.size();//记录每一层的元数个数
        while (sz--) {
                int cur = pq.front(); pq.pop();
            for (遍历cur所有的相邻节点nex) {
                if (nex节点有效 && vis[nex] == 0) {
                    vis[nex] = 1;
                    pq.push(nex)
                }
            } // end for
        } // end inner while
        level++;

    } // end outer while
}

lc 102 二叉树的层序遍历

二叉树的遍历递归与迭代实现_第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) {
        queue<TreeNode *>que;
        vector<vector<int>>res;
        if(root==NULL) return res;
        que.push(root);
        while(!que.empty()){
 // 这里一定要使用固定大小size,定义一个变量,不要使用que.size(),因为que.size是不断变化的
            int sz=que.size();
            vector<int>ans;
            while(sz--){
                TreeNode *cur=que.front();
                que.pop();
                ans.push_back(cur->val);
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            res.push_back(ans);
        }
        return res;
    }
};

lc 637.二叉树的层平均值

二叉树的遍历递归与迭代实现_第2张图片

/**
 * 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<double> averageOfLevels(TreeNode* root) {
        vector<double>res;
        if(root==NULL) return res;
        queue<TreeNode *>que;
        que.push(root);
        while(!que.empty()){
            int sz=que.size();
            double sum=0;
            int tmp=sz;
            while(sz--){
                TreeNode *cur=que.front();
                que.pop();
                sum+=cur->val;
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            res.push_back(sum/tmp);
        }
        return res;
    }
};

lc 429.N叉树的层序遍历

二叉树的遍历递归与迭代实现_第3张图片

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector children;

    Node() {}

    Node(int _val) {
        val = _val;
    }

    Node(int _val, vector _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>>res;
        if(root==NULL) return res;
        queue<Node *>que;
        que.push(root);
        while(!que.empty()){
            vector<int>ans;
            int sz=que.size();
            while(sz--){
                Node *cur=que.front();
                que.pop();
                ans.push_back(cur->val);
                //记录每层孩子的个数
                int cur_size=cur->children.size();
                //循环遍历孩子,将孩子加入队列中
                for(int i=0;i<cur_size;i++){
                   if(cur->children[i])  que.push(cur->children[i]);
                }
                
            }
            res.push_back(ans);
        }
        return res;

        
    }
};

lc 515.在每个树行中找到最大值

二叉树的遍历递归与迭代实现_第4张图片

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int>ans;
        if(root==nullptr) return ans;
        queue<TreeNode *>que;
        que.push(root);
        while(!que.empty()){
            int sz=que.size();
            //注意测试用例中有负数,初始化为最小值
            int tmp=INT_MIN,max_val=INT_MIN;
            while(sz--){
                TreeNode *cur=que.front();
                que.pop();
                tmp=cur->val;
                max_val=max(tmp,max_val);
                if(cur->left) que.push(cur->left);
                if(cur->right) que.push(cur->right);
            }
            ans.push_back(max_val);
        }
        return ans;
    }
};

你可能感兴趣的:(二叉树,二叉树,算法,数据结构)