树
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
    TreeNode() : TreeNode(0, nullptr, nullptr) {}
    TreeNode(int x) : TreeNode(x, nullptr, nullptr) {}
};
树的遍历
94-二叉树中序遍历、144-二叉树前序遍历、145-二叉树后序遍历
几乎所有的二叉树问题,都可以用 DFS 来解决。

class Solution {
public:
    void dfs(TreeNode *root, vector<int> &res) {
        if (!root) return;
        // preorder
        dfs(root->left, res);
        res.push_back(root->val); // inorder
        dfs(root->right, res);
        // postorder
    }
    vector<int> inorderTraversal(TreeNode *root) {
        vector<int> res;
        dfs(root, res);
        return res;
    }
};
递归实现时,是 函数调用自身,一层层地嵌套下去,操作系统自动帮我们用栈来保存了每个调用的函数;如果不用递归实现,我们可以用栈来模拟这个调用过程。(这种方法比较不直观,且前序、中序、后序不一样,很难记忆)
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        while (root || stk.size() != 0) {
            while (root) {
                stk.push(root);
                root = root->left;
            }
            root = stk.top();
            stk.pop();
            res.push_back(root->val);
            root = root->right;
        }
        return res;
    }
};
102-二叉树层序遍历
在实际使用中,我们用 DFS 的时候远远多于 BFS。不过,某些场景是 DFS 做不到的,只能使用 BFS,比如“层序遍历”。
BFS 会用到队列数据结构:
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode *root) {
        vector<vector<int>> res;
        if (!root) return res;
        queue<TreeNode *> q;
        q.push(root);
        while (!q.empty()) {
            vector<int> level;
            int size = static_cast<int>(q.size());
            for (int i = 0; i < size; ++i) {
                TreeNode *front = q.front();
                q.pop();
                level.push_back(front->val);
                if (front->left) {
                    q.push(front->left);
                }
                if (front->right) {
                    q.push(front->right);
                }
            }
            res.push_back(level);
        }
        return res;
    }
};
429-N 叉树的层序遍历
扩展到 N 叉树的层序遍历:
class Solution {
public:
    vector<vector<int>> levelOrder(Node *root) {
        vector<vector<int>> res;
        if (!root) return res;
        queue<Node *> queue;
        queue.push(root);
        while (!queue.empty()) {
            vector<int> level;
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                root = queue.front();
                queue.pop();
                level.push_back(root->val);
                for (Node *node : root->children) {
                    queue.push(node);
                }
            }
            res.push_back(level);
        }
        return res;
    }
};
199-二叉树的右视图
第一种思路,dfs 前序遍历,我们按照根结点 -> 右子树 -> 左子树的顺序访问,就可以保证每层都最先访问最右边的节点。
关键是,如何判断每层访问的第一个节点呢?可以用遍历深度来限定。由于每层只取最右边节 点的值加入 res 数组,若数组长度与遍历深度相同,说明是该层访问的第一个节点,把它加到 res 数组中。
class Solution {
public:
    void dfs(TreeNode *root, vector<int> &res, int level) {
        if (!root) return;
        if (res.size() == level)
            res.push_back(root->val);
        dfs(root->right, res, level + 1);
        dfs(root->left, res, level + 1);
    }
    vector<int> rightSideView(TreeNode *root) {
        vector<int> res;
        dfs(root, res, 0);
        return res;
    }
};
第二种思路,bfs 层序遍历:
class Solution {
public:
    vector<int> rightSideView(TreeNode *root) {
        vector<int> res;
        if (!root) return res;
        queue<TreeNode *> queue;
        queue.push(root);
        int level = 0;
        while (!queue.empty()) {
            int size = queue.size();
            for (int i = 0; i < size; ++i) {
                root = queue.front();
                queue.pop();
                if (res.size() == level)
                    res.push_back(root->val);
                if (root->right)
                    queue.push(root->right); // 先放右边的节点
                if (root->left)
                    queue.push(root->left);
            }
            ++level;
        }
        return res;
    }
};
124-二叉树中的最大路径和
本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。
找到递归结构是本题的关键!定义函数 dfs(root),它的返回值是以 root 为起点的树的最大路径和。
那么,以 root 为起点的树的最大路径和 = root->val + max(root->left 或 0,root->right 或 0),这是一个后序遍历!
注意,计算最大和这种问题,都需要考虑负数的情况,如果树的左/右支的最大和为负数,那么我们会“剪枝”。
本题中路径可以是任意节点出发到任意节点,不一定要以根结点为起点。因此,在执行每一次递归时,需要将 root 作为“连接点”,计算(根节点 + 左子树最大路径和 + 右子树最大路径和)是否成为全局最优解。
class Solution {
public:
    int dfs(TreeNode *root, int &res) {
        if (!root) return 0;
        int left = max(0, dfs(root->left, res)); // 左子树的最大路径和,或 0(剪枝)
        int right = max(0, dfs(root->right, res)); // 右子树的最大路径和,或 0(剪枝)
        res = max(res, left + right + root->val);
        return max(left, right) + root->val;
    }
    int maxPathSum(TreeNode *root) {
        int res = root->val;
        dfs(root, res);
        return res;
    }
};
105-从前序与中序遍历序列构造二叉树
树中没有重复元素。
只有中序+前序、中序+后序可以唯一确定一棵二叉树。
根据前序遍历得到根节点 ,然后在中序遍历中找到根节点的位置,它的左边就是左子树的节点,右边就是右子树的节点。这很明显是一个前序遍历的过程。

class Solution {
public:
    TreeNode* build(vector<int>& preorder, int preLeft, int preRight, vector<int>& inorder, int inLeft, int inRight, unordered_map<int, int> &um) {
        if (preLeft > preRight || inLeft > inRight) return nullptr;
        TreeNode *root = new TreeNode(preorder[preLeft]);
        int pIndex = um[root->val];
        root->left = build(preorder, preLeft + 1, preLeft + pIndex - inLeft, inorder, inLeft, pIndex - 1, um);
        root->right = build(preorder, preLeft + pIndex - inLeft + 1, preRight, inorder, pIndex + 1, inRight, um);
        return root;
    }
    TreeNode *buildTree(vector<int>& preorder, vector<int>& inorder) {
        unordered_map<int, int> um;
        for (int i = 0; i < inorder.size(); ++i) {
            um[inorder[i]] = i; // value -> position
        }
        return build(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1, um);
    }
};
106-从中序与后序遍历序列构造二叉树
根 - 左 - 右,前序遍历。
class Solution {
public:
    TreeNode *build(vector<int>& inorder, int inLeft, int inRight, vector<int>& postorder, int postLeft, int postRight, unordered_map<int, int> &um) {
        if (inLeft > inRight || postLeft > postRight) return nullptr;
        TreeNode *root = new TreeNode(postorder[postRight]);
        int pIndex = um[root->val];
        root->left = build(inorder, inLeft, pIndex - 1, postorder, postLeft, postLeft + pIndex - inLeft - 1, um);
        root->right = build(inorder, pIndex + 1, inRight, postorder, postLeft + pIndex - inLeft, postRight - 1, um);
        return root;
    }
    TreeNode *buildTree(vector<int>& inorder, vector<int>& postorder) {
        unordered_map<int, int> um;
        for (int i = 0; i < inorder.size(); ++i) {
            um[inorder[i]] = i;
        }
        return build(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1, um);
    }
};
100-相同的树
根节点相同、左子树相同、右子树也相同,这是一个前序遍历。
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (!p && !q) return true;
        if (p && q) {
            if (p->val != q->val) return false;
            if (!isSameTree(p->left, q->left)) return false;
            if (!isSameTree(p->right, q->right)) return false;
            return true;
        }
        return false;
    }
};
101-对称二叉树
若 p 和 q 相同,p.left 和 q.right 相同,p.right 和 q.left 相同,则为对称,这是一个前序遍历。
class Solution {
public:
    bool dfs(TreeNode *p, TreeNode *q) {
        if (!p && !q) return true;
        if (p && q) {
            if (p->val != q->val) return false;
            if (!dfs(p->left, q->right)) return false;
            if (!dfs(p->right, q->left)) return false;
            return true;
        }
        return false;
    }
    bool isSymmetric(TreeNode* root) {
        if (!root) return true;
        return dfs(root->left, root->right);
    }
};