【代码随想录】第8章 二叉树(上)

简介: 【代码随想录】第8章 二叉树

第8章 二叉树


1. 二叉树理论基础


二叉树种类


(1)满二叉树


(2)完全二叉树


(3)二叉搜索树


或称二叉查找树


中序遍历是递增序列



(4)平衡二叉搜索树



二叉树的存储方式


可以链式存储,也可以顺序存储


链式存储用指针,顺序存储用数组


顺序存储的元素在内存是连续分布的,链式存储则是通过指针把分布在散落在各个地址的结点串联在一起




用数组来存储二叉树如何遍历的呢?


如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。


二叉树的遍历


深度优先遍历:都有递归和迭代两种方法


  • 前序遍历:中左右


  • 中序遍历:左中右


  • 后序遍历:左右中



广度优先遍历


  • 层次遍历(迭代法)


二叉树的定义


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) {}
}


2. 二叉树的遍历


144. 二叉树的前序遍历


94. 二叉树的中序遍历


145. 二叉树的后序遍历


二叉树的递归遍历


递归三要素:


  1. 确定递归函数的参数和返回值:void preorder(TreeNode *root, vector<int>& res)


  1. 确定终止条件:if(cur == nullptr) return;


  1. 确定单层递归的逻辑


时间复杂度:O(n)


空间复杂度:O(n)


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


实现不同的遍历顺序通过修改单层递归的逻辑实现


二叉树的迭代遍历


通过栈实现


时间复杂度:O(n)


空间复杂度:O(n)


(1)前序遍历


class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> s;
        if(root == nullptr)  return res;
    s.push(root);  //压入根结点
        while (!s.empty()){  //入栈是 根右左
            TreeNode* node = s.top();  s.pop();
            res.push_back(node->val);                 //中
            if (node->right) stk.push(node->right);   //右
            if (node->left)  stk.push(node->left);    //左
        }
        return res;
    }
};


(2)中序遍历



class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> s;
        TreeNode* cur = root;
        while (cur != nullptr || !s.empty()) {
            if (cur != nullptr) {               // 指针来访问节点,访问到最底层
                s.push(cur);                    // 将访问的节点放进栈
                cur = cur->left;                // 左
            } 
            else {
                cur = s.top();   s.pop();       // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
                res.push_back(cur->val);        // 中
                cur = cur->right;               // 右
            }
        }
        return res;
    }
};


(3)后序遍历


跟前序的很像,中序是三个当中差距最大的


class Solution {
public:
    //后续迭代最难,有些结点反复进出栈
    vector<int> postorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> s;
        if (root == nullptr)   return res;
        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);   //右  
        }
        reverse(res.begin(), res.end()); // 将结果反转之后就是左右中的顺序了
        return res;
    }
};


二叉树的层序遍历


102. 二叉树的层序遍历


通过队列实现


class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector <vector <int>> res;
        if (root == nullptr)  return res;    //排除空二叉树情况
        queue <TreeNode*> q;
        q.push(root);    //根结点入队
        while (!q.empty()) {
            int currentLevelSize = q.size();   //记录当前层的结点个数
            res.push_back(vector <int>());
            for (int i = 1; i <= currentLevelSize; ++i) {
                auto node = q.front();             //获取队头元素
                q.pop();                           //删除队头元素
                res.back().push_back(node->val);   //往ret的最后一个容器中压元素
                if (node->left)  q.push(node->left);  
                if (node->right) q.push(node->right);
            }
        }
        return res;
    }
};


107. 二叉树的层序遍历 II【中等】



思路:把层序遍历的结果reverse一下再输出


reverse(res.begin(), res.end());


完整代码不记录


199. 二叉树的右视图【中等】



思路:层次遍历存储每层最后一个元素


class Solution {
public:
  vector<int> rightSideView(TreeNode* root) {
    vector<int> res;
    if (root == nullptr) return res;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
      int currentSize = q.size();
      for (int i = 1; i <= currentSize; ++i) {
        auto node = q.front();
        q.pop();
        if (i == currentSize)  res.push_back(node->val);
        if (node->left)  q.push(node->left);
        if (node->right) q.push(node->right);
      }
    }
    return res;
  }
};


637. 二叉树的层平均值【简单】



class Solution {
public:
  vector<double> averageOfLevels(TreeNode* root) {
    vector<double> res;
    if (root == nullptr) return res;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
      int currentSize = q.size();
      double num = 0;    //注意是double
      for (int i = 1; i <= currentSize; ++i) {
        auto node = q.front();
        q.pop();
        num += node->val;
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
      }
            res.push_back(num / currentSize);
    }
    return res;
  }
};


429. N 叉树的层序遍历【中等】



/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> children;
    Node() {}
    Node(int _val) {
        val = _val;
    }
    Node(int _val, vector<Node*> _children) {
        val = _val;
        children = _children;
    }
};
*/
class Solution {
public:
    vector<vector<int>> levelOrder(Node* root) {
        vector<vector<int>> res;
        if(root == nullptr) return res;
        queue<Node*> q;
        q.push(root);
        while(!q.empty()){
            res.push_back(vector<int> ());
            int currentSize = q.size();
            for(int i = 1; i <= currentSize; ++i){
                auto node = q.front();
                q.pop();
                res.back().push_back(node->val);
                for(auto i : node->children){
                    q.push(i);
                }
            }
        }
        return res;
    }
};


515. 在每个树行中找最大值【中等】



class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        vector<int> res;
        if(root == nullptr) return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int currentSize = q.size();
            int nowMax = INT_MIN;
            for(int i = 1; i <= currentSize; ++i){
                auto node = q.front();
                q.pop();
                nowMax = max(node->val, nowMax);
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            res.push_back(nowMax);
        }
        return res;
    }
};


116. 填充每个节点的下一个右侧节点指针【中等】



/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;
    Node() : val(0), left(NULL), right(NULL), next(NULL) {}
    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}
    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
  Node* connect(Node* root) {
    if (root == nullptr) return root;
    queue<Node*>q;
    q.push(root);
    while (!q.empty()) {
      int current_level_size = q.size();
      for (int i = 1; i <= current_level_size; ++i) {
        auto node = q.front();
        q.pop();
        if (i < current_level_size) node->next = q.front();   //实现next指向
        else node->next = NULL;
        if (node->left) q.push(node->left);
        if (node->right) q.push(node->right);
      }
    }
        return root;
  }
};


117. 填充每个节点的下一个右侧节点指针 II【中等】


与116题做法一模一样,这里写了代码随想录的解法;116题写的是我自己习惯的解法。


class Solution {
public:
    Node* connect(Node* root) {   
        if(root == nullptr) return root;
        queue<Node*> q;
        q.push(root);
        while (!que.empty()) {
            int currentSize = que.size();
            Node* nodePre;
            Node* node;
            for (int i = 1; i <= currentSize; i++) {
                if (i == 0) {
                    nodePre = q.front(); // 取出一层的头结点
                    q.pop();
                    node = nodePre;
                    continue;
                } 
                node = q.front();
                q.pop();
                nodePre->next = node;    // 本层前一个节点next指向本节点
                nodePre = nodePre->next;
                if (node->left) q.push(node->left);
                if (node->right) q.push(node->right);
            }
            nodePre->next = NULL;    // 本层最后一个节点指向NULL
        }
        return root;
    }
};


104. 二叉树的最大深度【简单】



class Solution {
public:
    int maxDepth(TreeNode* root) {
        int res = 0;
        if(root == nullptr) return res;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int currentSize = q.size();
            for(int i = 1; i <= currentSize; ++i){
                auto node = q.front();
                q.pop();
                if(node->left) q.push(node->left);
                if(node->right) q.push(node->right);
            }
            res++;
        }
        return res;
    }
};


111. 二叉树的最小深度【简单】



class Solution {
public:
    int minDepth(TreeNode* root) {
        int res = 0;
        if (root == nullptr) return res;
        queue <TreeNode*> q;
        q.push(root);    //根结点入队
        while (!q.empty()) {
            int currentLevelSize = q.size();   //记录当前层的结点个数
            res++;
            for (int i = 1; i <= currentLevelSize; i++) {  //队列弹出当前层所有元素   并压入下一层所有元素
                auto node = q.front();
                q.pop();
                if (node->left == nullptr && node->right == nullptr) {
                    return res;
                }
                if (node->left)  q.push(node->left);
                if (node->right) q.push(node->right);
            }
        }
        return res;
    }
};


3. 翻转二叉树


226. 翻转二叉树【简单】


相同题目:剑指 Offer 27. 二叉树的镜像



迭代:


广度优先遍历,即层次遍历


class Solution {
public:
  TreeNode* invertTree(TreeNode* root) {
    if (root == nullptr) return root;
    queue<TreeNode*> q;
    q.push(root);
    while (!q.empty()) {
      auto node = q.front();              
      q.pop();
            swap(node->left, node->right);
      if (node->left) q.push(node->left); 
      if (node->right) q.push(node->right);
    }
    return root;
  }
};


深度优先遍历就是把queue换成stack


递归:


递归三部曲:


  1. 确定递归函数的参数和返回值:TreeNode* invertTree(TreeNode* root)


  1. 确定终止条件:if(root == nullptr) return NULL


  1. 确定单层递归的逻辑


class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root == nullptr) return NULL;
        swap(root->left, root->right);
        invertTree(root->left);
        invertTree(root->right);
        return root;
    }
};


上面是递归的前序遍历


递归的中序遍历这里是不行的


4. 对称二叉树


101. 对称二叉树【简单】


相同题目:剑指 Offer 28. 对称的二叉树



迭代:


class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if(root == nullptr) return true;
        queue<TreeNode*> q;
        q.push(root->left);
        q.push(root->right);
        while(!q.empty()){
            auto leftNode = q.front(); q.pop();
            auto rightNode = q.front(); q.pop();
            if(leftNode == nullptr && rightNode == nullptr) continue;   //少了报错
            if(leftNode == nullptr && rightNode != nullptr) return false;
            if(leftNode != nullptr && rightNode == nullptr) return false;
            if(leftNode->val != rightNode->val) return false;
            q.push(leftNode->left);
            q.push(rightNode->right);
            q.push(leftNode->right);
            q.push(rightNode->left);
        }
        return true;
    }
};


同样的queue改成stack也可以,其他原封不动


递归:


递归三部曲:


  1. 确定递归函数的参数和返回值:bool compare(TreeNode* left, TreeNode* right)


  1. 确定终止条件


  1. 确定单层递归逻辑


class Solution {
public:
    bool compare(TreeNode* left, TreeNode* right){  //1.确定递归函数的参数
        //2.确定终止条件
        if(left == nullptr && right == nullptr) return true;
        else if(left == nullptr && right != nullptr) return false;
        else if(left != nullptr && right == nullptr) return false;
        else if(left->val != right->val) return false;
        //3.返回值
        return compare(left->left, right->right) && compare(left->right, right->left);
    }
    bool isSymmetric(TreeNode* root) {
        if(root == nullptr) return true;
        return compare(root->left, root->right);
    }
};


100. 相同的树【简单】



递归:


class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == nullptr && q == nullptr) return true;
        else if(p == nullptr && q != nullptr) return false;
        else if(p != nullptr && q == nullptr) return false;
        else if(p->val != q->val) return false;
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};


迭代:


class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if(p == nullptr && q == nullptr) return true;
        if(p == nullptr || q == nullptr) return false;
        queue<TreeNode*> que;
        que.push(p);
        que.push(q);
        while(!que.empty()){
            auto pNode = que.front(); que.pop();
            auto qNode = que.front(); que.pop();
            if(pNode->val != qNode->val)  return false;
            auto pLeft = pNode->left, pRight = pNode->right, qLeft = qNode->left, qRight = qNode->right;
            if(pLeft == nullptr && qLeft != nullptr) return false;
            else if(pLeft != nullptr && qLeft == nullptr) return false;
            else if(pLeft != nullptr && qLeft != nullptr){
                que.push(pLeft);
                que.push(qLeft);
            }
            if(pRight == nullptr && qRight != nullptr) return false;
            else if(pRight != nullptr && qRight == nullptr) return false;
            else if(pRight != nullptr || qRight != nullptr) {
                que.push(pRight);
                que.push(qRight);
            }
        }
        return true;
    }
};



目录
相关文章
|
3月前
|
存储 算法 C语言
C语言手撕实战代码_二叉树_构造二叉树_层序遍历二叉树_二叉树深度的超详细代码实现
这段代码和文本介绍了一系列二叉树相关的问题及其解决方案。其中包括根据前序和中序序列构建二叉树、通过层次遍历序列和中序序列创建二叉树、计算二叉树节点数量、叶子节点数量、度为1的节点数量、二叉树高度、特定节点子树深度、判断两棵树是否相似、将叶子节点链接成双向链表、计算算术表达式的值、判断是否为完全二叉树以及求二叉树的最大宽度等。每道题目均提供了详细的算法思路及相应的C/C++代码实现,帮助读者理解和掌握二叉树的基本操作与应用。
|
6月前
代码随想录——双指针(一)
代码随想录——双指针(一)
代码随想录 Day21 - 二叉树(七)
代码随想录 Day21 - 二叉树(七)
39 0
|
7月前
|
算法
代码随想录Day34 LeetCode T343整数拆分 T96 不同的二叉搜索树
代码随想录Day34 LeetCode T343整数拆分 T96 不同的二叉搜索树
54 0
代码随想录 Day14 - 二叉树(一)
代码随想录 Day14 - 二叉树(一)
55 1
代码随想录 Day17 - 二叉树(四)
代码随想录 Day17 - 二叉树(四)
43 1
代码随想录 Day15 - 二叉树(二)
代码随想录 Day15 - 二叉树(二)
41 0
代码随想录 Day23 - 二叉树(九)
代码随想录 Day23 - 二叉树(九)
54 0
代码随想录 Day20 - 二叉树(六)
代码随想录 Day20 - 二叉树(六)
51 0
代码随想录 Day22 - 二叉树(八)
代码随想录 Day22 - 二叉树(八)
44 0