专题:二叉树递归遍历

1.二叉树的前序遍历 后序遍历 中序遍历 以及非递归写法

递归写法:

前序遍历

cpp 复制代码
class Solution {
    vector<int> res{};
    void dfs(TreeNode* root) {
      if(root==nullptr) return;
      res.push_back(root->val);
      dfs(root->left);
      dfs(root->right);
      return;
    }
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
      dfs(root);
      return res;
    }
};

后序遍历

cpp 复制代码
class Solution {
    vector<int> res{};
    void dfs(TreeNode* root) {
      if(root==nullptr) return;
      dfs(root->left);
      dfs(root->right);
      res.push_back(root->val);
      return;
    }
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
      dfs(root);
      return res;
    }
};

中序遍历

cpp 复制代码
class Solution {
    vector<int> res{};
    void dfs(TreeNode* root) {
      if(root==nullptr) return;
      dfs(root->left);
      res.push_back(root->val);
      dfs(root->right);
      return;
    }
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
      dfs(root);
      return res;
    }
};

非递归写法:

前序遍历

cpp 复制代码
class Solution {


public:
    vector<int> preorderTraversal(TreeNode* root) {
      // 非递归写法 根左右
      vector<int> res{};
      stack<TreeNode*> s;
      if(root == nullptr) return {};
      s.push(root);
      while(!s.empty()){
        auto node = s.top();
        s.pop();
        
        res.push_back(node->val);
        if(node->right){
          s.push(node->right);
        }
        if(node->left){
          s.push(node->left);
        }
        // cout<<"{";
        // for(auto val :s){
        //   cout<<","<<val<<endl;
        // }
        // cout<<"}"<<endl;
      }
      return res;
    }
};

后续遍历 非递归写法就是 根右左 最后reverse就是 左右根了 注意和前序遍历非递归写法入栈顺序不同 前序非递归写法后入左孩子 后续需后入右孩子

cpp 复制代码
class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
      // 非递归写法 根左右
      vector<int> res{};
      stack<TreeNode*> s;
      if(root == nullptr) return {};
      s.push(root);
      while(!s.empty()){
        auto node = s.top();
        s.pop();
        
        res.push_back(node->val);
        if(node->left){
          s.push(node->left);
        }
        if(node->right){
          s.push(node->right);
        }
        // cout<<"{";
        // for(auto val :s){
        //   cout<<","<<val<<endl;
        // }
        // cout<<"}"<<endl;
      }
      reverse(res.begin(),res.end());
      return res;
    }
};

中序遍历 注意这里非递归写法的前序和后序最大的不同是 入栈第一个元素 并是不取出来的第一个元素

cpp 复制代码
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res{};
      // 非递归写法 左根右
      stack<TreeNode*> s;
      auto cur = root;
      if(cur==nullptr){
        return {};
      }
      while(!s.empty()||cur!=nullptr){
        while(cur){
            s.push(cur);
            cur=cur->left;
        }
        TreeNode * pnode = s.top();
        s.pop();
        res.push_back(pnode->val);
        // cout<<"pnode->val:"<<pnode->val<<"   s.size():"<<s.size()<<endl;
        if(pnode->right){
           cur = pnode->right;
        }
      }
      return res;
    }
};

ps:递归写法后续遍历不使用全局变量写法

```cpp
class Solution {
    vector<int> res{};
    void dfs(TreeNode* root) {
      if(root==nullptr) return;
      dfs(root->left);
      dfs(root->right);
      res.push_back(root->val);
      return;
    }
    
public:
    vector<int> postorderTraversal(TreeNode* root) {
      if(root==nullptr) return{};
      vector<int> le = postorderTraversal(root->left);
      vector<int> ri = postorderTraversal(root->right);

      vector<int> cur{};
      if(!le.empty()){
         cur.insert(cur.begin(), le.begin(),le.end());
      }
      if(!ri.empty()){
         cur.insert(cur.end(), ri.begin(),ri.end());
      }
      cur.push_back(root->val);
      // cout<<"le.size:"<<le.size()<<"    ri.size:"<<ri.size()<<endl;
      // cout<<"root:val:"<<root->val<<endl<<endl;
      return cur; 
    }
};
**2.[二叉树层序遍历](https://leetcode.cn/problems/binary-tree-level-order-traversal/description/)**

```cpp
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        // 要用队列
        deque<TreeNode*> qu;
        vector<vector<int>> res_vv{};
        if(root==nullptr){
           return res_vv;
        }
        qu.push_back(root);
        while(!qu.empty()){
          int size = qu.size();
          vector<int> res{};
          for(int i=0;i<size;i++){
            auto pnode = qu.front();
            qu.pop_front();
            res.push_back(pnode->val);

            // 该节点是否存在左右节点
            if(pnode->left){
              qu.push_back(pnode->left);
            }
            if(pnode->right){
              qu.push_back(pnode->right);
            }
          }
          res_vv.push_back(res);
        }
        return res_vv;
    }
};

上面代码需要注意的是 int size = qu.size(); 每层遍历的时候需要重新取一次 for(int i=0;i<qu.size();i++) 因为循环体内队列里面添加元素 qu.size()会实时变化的

3.翻转二叉树

cpp 复制代码
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        //   确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
        if(root==nullptr) return nullptr;
        auto temp = root->left;
        root->left = root->right;
        root->right = temp;

        invertTree(root->left);
        invertTree(root->right);
        return root;
    }
};

4.对称二叉树

cpp 复制代码
class Solution {
public:
    bool dfs(TreeNode* leftroot, TreeNode* rightroot){
        //   确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
      if(leftroot == nullptr && rightroot == nullptr) return true;
      if(leftroot != nullptr && rightroot == nullptr) return false;
      if(leftroot == nullptr && rightroot != nullptr) return false;
      //   if(leftroot != nullptr && rightroot != nullptr)
      if(leftroot->val != rightroot->val) return false;
      // 后续遍历
      return dfs(leftroot->left,rightroot->right) &&  dfs(leftroot->right,rightroot->left);
    } 
    bool isSymmetric(TreeNode* root) {
        // 自上而下 但是中途会有提前结束 
        if(root==nullptr) return true;
        return dfs(root->left,root->right);
    }
};

这里遍历顺序可以说是后续遍历 但是中途存在提前退出循环的可能 表面看起来是自顶向下(前序遍历)

5.二叉树的最大深度

cpp 复制代码
class Solution {
public:
    int maxDepth(TreeNode* root) {
        // 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
        if(root == nullptr) return 0;
        if(root->left ==nullptr && root->right ==nullptr) return 1;
        int leftlen = maxDepth(root->left);
        int rightlen = maxDepth(root->right);
        int rootlen = max(leftlen,rightlen)+1;
        return rootlen;
    }
};

6.二叉树的最小深度

方法一 直接遍历到空节点

cpp 复制代码
public:
    int minDepth(TreeNode* root) {
        // 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
        if(root ==nullptr) return 0;
        int leftmin = minDepth(root->left);
        int rightmin = minDepth(root->right);
        int curtmin = 0;
        if(leftmin == 0){
            curtmin = rightmin+1;
        }else if(rightmin == 0){
            curtmin = leftmin+1;
        }else {
            curtmin = min(leftmin, rightmin) +1;
        }
        return curtmin;
    }
};

明显需要后续遍历 每次取左右两个做小的深度 但是我们要注意一点 上面的遍历方式一定会遍历到某个节点的左孩子/右孩子节点是空 此时不能直接取最小值 需要做特殊处理

cpp 复制代码
        if(leftmin == 0){
            curtmin = rightmin+1;
        }else if(rightmin == 0){
            curtmin = leftmin+1;
        }else {
            curtmin = min(leftmin, rightmin) +1;
        }

方法二 不遍历空节点 (递归需要保证头结点不为空 因此需要在主函数中直接判断)

cpp 复制代码
class Solution {
    int posdfs(TreeNode* root) {
        // 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
        if(root->left==nullptr && root->right==nullptr) return 1;
        int  leftmin, rightmin;
        cout<<"start"<<endl;
        if(root->left != nullptr && root->right == nullptr){
            leftmin = minDepth(root->left);
            // cout<<"leftmin:"<<leftmin+1<<endl;
            return leftmin+1;
        }
        if(root->left == nullptr && root->right != nullptr){
            rightmin = minDepth(root->right);
            // cout<<"rightmin:"<<rightmin+1<<endl;
            return rightmin+1;
        }
        // 左右都有高度
        int rootlen;
        leftmin = minDepth(root->left);
        rightmin = minDepth(root->right);
        rootlen = min(leftmin, rightmin)+1;
        // cout<<"rootlen:"<<rootlen<<endl<<endl;
        return rootlen;
    }
public:
    int minDepth(TreeNode* root) {
      if(root==nullptr) return 0;
      return posdfs(root);
    }
};

7.完全二叉树的节点个数

我们先看看普通二叉树的节点数目写法 后序遍历

cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root==nullptr) return 0;
        int leftcount = countNodes(root->left);
        int rightcount = countNodes(root->right);
        int rootcount = leftcount+rightcount+1;
        return rootcount;
    }
};

完全二叉树写法(其实也就是 结束条件里面有中途rerurn逻辑场景)

cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {

        // 题目说是完全二叉树
        // 确定是否需要返回值(和题目的递归函数函数是否有返回值无关) 确定遍历顺序(有返回值接的需要接住) 确定结束条件(注意是否存在中途直接return)  确定单层循环逻辑
        if (root == nullptr) return 0;
        int leftdep=1,rightdep=1;
        TreeNode* lpnode = root, *rpnode = root;
        while(lpnode->left!=nullptr){
            lpnode = lpnode->left;
            leftdep++;
        }
        while(rpnode->right!=nullptr){
            rpnode = rpnode->right;
            rightdep++;
        }
        if (leftdep == rightdep) {
            // 这个节点(包含当前节点)以下都是满二叉树
            int curcount = (2<<(leftdep-1)) -1;
            return curcount;
        }
        int leftcount = countNodes(root->left);
        int rightcount = countNodes(root->right);
        int curcount = leftcount + rightcount + 1;
        return curcount;
    }
};

8.平衡二叉树

9.二叉树的所有路径

10.左叶子之和

11.找树左下角的值

12.路径总和

13.从中序与后序遍历序列构造二叉树

14.最大二叉树

相关推荐
chenziang111 分钟前
leetcode hot100 环形链表2
算法·leetcode·链表
Captain823Jack2 小时前
nlp新词发现——浅析 TF·IDF
人工智能·python·深度学习·神经网络·算法·自然语言处理
Captain823Jack2 小时前
w04_nlp大模型训练·中文分词
人工智能·python·深度学习·神经网络·算法·自然语言处理·中文分词
是小胡嘛3 小时前
数据结构之旅:红黑树如何驱动 Set 和 Map
数据结构·算法
m0_748255023 小时前
前端常用算法集合
前端·算法
呆呆的猫3 小时前
【LeetCode】227、基本计算器 II
算法·leetcode·职场和发展
Tisfy3 小时前
LeetCode 1705.吃苹果的最大数目:贪心(优先队列) - 清晰题解
算法·leetcode·优先队列·贪心·
余额不足121384 小时前
C语言基础十六:枚举、c语言中文件的读写操作
linux·c语言·算法
火星机器人life6 小时前
基于ceres优化的3d激光雷达开源算法
算法·3d
虽千万人 吾往矣6 小时前
golang LeetCode 热题 100(动态规划)-更新中
算法·leetcode·动态规划