二叉树

前序位置的代码在刚刚进入一个二叉树节点的时候执行;

后序位置的代码在将要离开一个二叉树节点的时候执行;

中序位置的代码在一个二叉树节点左子树都遍历完,即将开始遍历右子树的时候执行。

image

void traverse(tree_node root)
{
  if(root==null)  return;
  //前序位置
  traverse(root.left);
  //中序位置
  traverse(root.right);
  //后序位置
}

前序位置的代码只能从函数参数中获取父节点传递来的数据,而后序位置的代码不仅可以获取参数数据,还可以获取到子树通过函数返回值传递回来的数据。

题目和子树有关,那大概率要给函数设置合理的定义和返回值,在后序位置写代码了。

两类思路: 遍历一遍二叉树;通过分解问题计算出答案. 分别对应回溯/动态规划
(回溯无返回值, 动态规划有返回值)

二叉树遍历

层序

102. 二叉树的层序遍历
BPS

/**
 * 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<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> vec_all;
        //如果为空, 直接返回
        if(root == nullptr) return vec_all;
        //使用队列保存每层的所有节点,每次把队列里的原先所有节点进行出队列操作,再把每个元素的非空左右子节点进入队列。因此即可得到每层的遍历。
        //怎么知道是在这一层呢? 关键是每一次循环都设一个size,没pop一次就size--
        queue<TreeNode*> que;
        TreeNode* cur = root;
        //第一层入队
        que.emplace(cur);
        //如果队列为空, 就退出循环
        while(!que.empty())
        {
            //如果这一层的元素遍历完了, 就退出循环
            int size = que.size();
            //存放一层的数据
            vector<int> vec;
            while(size--)
            {
                //插入队首元素 
                vec.emplace_back(que.front()->val);
                //如果队首有子节点, 插入到que中
                if(que.front()->left!=nullptr) que.emplace(que.front()->left);
                if(que.front()->right!=nullptr) que.emplace(que.front()->right);
                //删除队首节点
                que.pop();
            }
            //将一层放入vec_all中汇总
            vec_all.emplace_back(vec);
        }
        return vec_all;
    }
};

前序

144. 二叉树的前序遍历
递归

class Solution {
public:
    //辅助函数
    void pre_order(vector<int>& vec, TreeNode* cur)
    {
        //递归终止
        if(cur == nullptr) return;
        //前序遍历就是最先打印出来
        vec.push_back(cur->val);
        //递归关系
        pre_order(vec, cur->left);
		// |中序位置|
        pre_order(vec, cur->right);
		// |后序位置|
    }
    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> vec;
        TreeNode* cur = root;
        pre_order(vec, cur);
        return vec;
    }
};

迭代

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        if(root==nullptr) return {};
        vector<int> res;
        stack<TreeNode*> st;

        st.push(root);
        while(!st.empty())
        {
            TreeNode* node = st.top();
            st.pop();
            res.emplace_back(node->val);
            if(node->right!=nullptr)  st.push(node->right);
            if(node->left!=nullptr)  st.push(node->left);
        }
        return res;
    }
};

中序

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> st;
        while(!st.empty()||root!=nullptr)
        {
            // 先往左, 遇到就入栈
            while(root!=nullptr)
            {
                st.push(root);
                root=root->left;
            }
            // 拿出中间
            root = st.top();
            st.pop();
            res.emplace_back(root->val);
            // 到右边
            root = root->right;
        }
        return res;
    }
};

后序

class Solution {
public:
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==nullptr)  return {};
        // 前序->中右左->再反过来->后序
        vector<int> res;
        stack<TreeNode*> st1;
        st1.push(root);
        while(!st1.empty())
        {
            TreeNode* node = st1.top();
            st1.pop();
            res.emplace_back(node->val);
            if(node->left!=nullptr)  st1.push(node->left);  //压栈: 左右 | 读取: 中右左-->reverse->左中右后序
            if(node->right!=nullptr)  st1.push(node->right);
        }
        reverse(res.begin(),res.end());
        return res;
    }
};

几个概念

完美二叉树/完全二叉树/满二叉树

其中, 满二叉树不用做到"除了最后一层, 之前的层都被填充"

底下这个来自SO大佬, 这是我看到的讲得最透彻的图了

Perfect Tree:

       x
     /   \
    /     \
   x       x
  / \     / \
 x   x   x   x
/ \ / \ / \ / \
x x x x x x x x
Complete Tree:

       x
     /   \
    /     \
   x       x
  / \     / \
 x   x   x   x
/ \ /
x x x
Strict/Full Tree:

       x
     /   \
    /     \
   x       x
  / \ 
 x   x 
    / \
    x x 

实践

700. 二叉搜索树中的搜索

/**
 * 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:
    TreeNode* searchBST(TreeNode* root, int val) {
        //这是二叉搜索树了, 每一次查找都是"有效的". 最后要么是找到了, 要么是找到null(此时也是root, 合并一下)
        if(root == nullptr||root->val == val) return root;
        
        //如果值<该节点的值, 向左搜索, 否则向右搜索
        if(val<root->val) return searchBST(root->left,val);
        return searchBST(root->right,val);
    }
};

110. 平衡二叉树

/**
 * 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:
    //递归问题:返回左右子树的高度差状态, 如果高度差<2则返回最大的那个(子树的深度, 算最深的那个); 如果高度差>=2则返回-1
    int recur(TreeNode* cur)
    {
        //递归终止: 越过子节点 递归回来一个信息: 目前的子节点的深度为0
        if(cur == nullptr) return 0;
        //左右子树高度left==-1
        int left = recur(cur->left);
        if(left == -1) return -1;       //只要有一个节点的左右子树高度差的绝对值>=2, 这个就是非平衡树, 这个返回-1就病毒一样传播
        int right = recur(cur->right);
        if(right == -1) return -1;
        //递归到最深的时候肯定要越过子节点, 如果目前没有发现是非平衡树, 则返回目前的深度+1 
        //返回的时候已经把左右节点都递归完了
        return abs(left - right)<2 ? max(left,right) + 1:-1;
    }

    bool isBalanced(TreeNode* root) {
        //递归关系 当root左右子树高度差<2 or 左右子树高度差>=2 返回 -1 此子树不是平衡树.
        //左右子树的高度怎么得到呢, 根据下一层递归返回的深度
        return recur(root)!=-1;
    }
};
posted @ 2023-05-17 12:28  无形深空  阅读(1)  评论(0编辑  收藏  举报