树
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);
}
};