解题思路:
- 是否可以通过 遍历 \color{OrangeRed}遍历 遍历一遍二叉树得到答案? 如果可以,用一个traverse函数配合外部变量实现
- 是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案? 分解问题 \color{OrangeRed}分解问题 分解问题
- 不管哪种模式,需要思考:如果单独抽出一个节点,它需要做什么事情,需要在什么时候(前/中/后序位置)做
完全二叉树:
- 定义:在完全二叉树中,除了最底层节点可能没填满之外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层的最左边,若最底层为h层,则该层包含 1 − p o w ( 2 , h − 1 ) \color{OrangeRed}1 - pow(2, h-1) 1−pow(2,h−1) 个节点
- 完全二叉树有 2 d − 1 \color{OrangeRed}2^d - 1 2d−1个节点
Leetcode 222. Count Complete Tree Nodes
class Solution {
public:
int countNodes(TreeNode* root) {
// Time Complexity: O(log n x log n)
if (root == nullptr)
{
return 0;
}
TreeNode* l = root->left;
TreeNode* r = root->right;
int l_depth = 0, r_depth = 0;
while (l)
{
l_depth ++;
l = l->left;
}
while (r)
{
r_depth ++;
r = r->right;
}
if (r_depth == l_depth)
{
return (2 << l_depth) - 1; // (2 << 1) = 2^2
}
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
Leetcode 110. Balanced Binary Tree
class Solution {
public:
// -1 means it's already not a balanced tree
int depth(TreeNode* root)
{
if (root == nullptr)
{
return 0;
}
int l = depth(root->left);
if (l == -1) return -1;
int r = depth(root->right);
if (r == -1) return -1;
return abs(l - r) > 1 ? -1 : 1 + max(l, r);
}
bool isBalanced(TreeNode* root) {
return depth(root) != -1;
}
};
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。 p r e o r d e r \color{OrangeRed} preorder preorder
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。 p o s t o r d e r \color{OrangeRed} postorder postorder
Leetcode 257 Binary Tree Path
B a c k t r a c k i n g : \color{OrangeRed} Backtracking: Backtracking: the use of string s, do not path by reference, otherwise it’s not backtracking
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
// pre-order
// recursive
helper(root, res, "");
return res;
}
void helper(TreeNode* root, vector<string>& res, string s)
{
if (!root->left && !root->right)
{
res.push_back(s + to_string(root->val));
return;
}
if (root->left) helper(root->left, res, s + to_string(root->val) + "->");
if (root->right) helper(root->right, res, s + to_string(root->val) + "->");
}
};
-
确定递归终止条件:
\color{OrangeRed} 确定递归终止条件:
确定递归终止条件:
本题应该在找到 l e a f n o d e \color{OrangeRed} leaf node leafnode的时候就开始结束的处理逻辑(把路径存到res)
if (!cur->left && !cur->right)
{
// handle the leaf node
}
- another method: iterative
以下code属于代码随想录:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> treeSt;// 保存树的遍历节点
stack<string> pathSt; // 保存遍历路径的节点
vector<string> result; // 保存最终路径集合
if (root == NULL) return result;
treeSt.push(root);
pathSt.push(to_string(root->val));
while (!treeSt.empty()) {
TreeNode* node = treeSt.top(); treeSt.pop(); // 取出节点 中
string path = pathSt.top();pathSt.pop(); // 取出该节点对应的路径
if (node->left == NULL && node->right == NULL) { // 遇到叶子节点
result.push_back(path);
}
if (node->right) { // 右
treeSt.push(node->right);
pathSt.push(path + "->" + to_string(node->right->val));
}
if (node->left) { // 左
treeSt.push(node->left);
pathSt.push(path + "->" + to_string(node->left->val));
}
}
return result;
}
};
Leetcode 404. Sum of Left Leaves
p o s t o r d e r \color{OrangeRed} postorder postorder
int sumOfLeftLeaves(TreeNode* root) {
if (root == nullptr)
{
return 0;
}
int left = 0;
if (root->left)
{
if (!root->left->left && !root->left->right)
{
left += root->left->val;
}
}
return left + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
}
Leetcode 513. Find Bottom Left Tree Value
思路: \color{OrangeRed} 思路: 思路: recursive/dfs, 先左后右,在每行第一个node更新max_depth和result,有回溯的思想
int depth = 0;
int max_depth = INT_MIN;
int res = 0;
int findBottomLeftValue(TreeNode* root) {
// level order traversal
if (root == nullptr)
{
return 0;
}
if (depth > max_depth && !root->left && !root->right)
{
max_depth = depth;
res = root->val;
return res;
}
//max_depth = max(max_depth, depth);
depth ++;
int left = findBottomLeftValue(root->left);
int right = findBottomLeftValue(root->right);
depth --;
return res;
}
以下代码属于代码随想录:
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
return;
}
if (root->left) {
traversal(root->left, depth + 1); // 隐藏着回溯
}
if (root->right) {
traversal(root->right, depth + 1); // 隐藏着回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
Leetcode 113. Path Sum II
class Solution {
public:
void helper(TreeNode* root, vector<vector<int>>& res, vector<int>& path, int cur, int target)
{
if (root == nullptr)
{
return;
}
if (root->left == nullptr && root->right == nullptr && cur + root->val == target)
{
path.push_back(root->val);
res.push_back(path);
path.pop_back();
return;
}
path.push_back(root->val);
if (root->left)
{
helper(root->left, res, path, cur + root->val, target);
}
if (root->right)
{
helper(root->right, res, path, cur + root->val, target);
}
path.pop_back();
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>> res;
vector<int> t;
helper(root, res, t, 0, targetSum);
return res;
}
};
Leetcode 114. Flatten Binary Tree To Linked List
思路: \color{OrangeRed} 思路: 思路: 分解问题
- 对于一个节点 n,可以执行以下流程:
1.1 flatten(n->left), flatten(n->right)
1.2 把n的右子树接到左子树的右下方,将整个左子树作为右子树
class Solution {
public:
void flatten(TreeNode* root) {
if (root == nullptr)
{
return;
}
flatten(root->left);
flatten(root->right);
if (root->left && root->right)
{
TreeNode* ptr = root->left;
while (ptr->right)
{
ptr = ptr->right;
}
ptr->right = root->right;
root->right = root->left;
root->left = nullptr;
}
else if (root->left)
{
root->right = root->left;
root->left = nullptr;
}
return;
}
};
Leetcode 106. Construct Binary Tree From Inorder and Postorder Traversal
思路:
\color{OrangeRed} 思路:
思路:
根据inorder和postorder的特征:
- postorder 的最后一个值是root
- 在inorder中找到root的index,root左侧为left subtree, root右侧为right subtree
- 在postorder中找到根据inorder找到的left subtree的最后一个数字,根据这个数字在postorder中划分left subtree和right subtree
- recursive 分解问题
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
// recursively build
if (inorder.size() == 0)
{
return nullptr;
}
int sz = postorder.size();
TreeNode* new_root = new TreeNode(postorder[sz - 1]);
auto iter = find(inorder.begin(), inorder.end(), postorder[sz - 1]);
vector<int> new_left_inorder(inorder.begin(), iter);
vector<int> new_left_post(postorder.begin(), postorder.begin() + new_left.size());
new_root->left = buildTree(new_left_inorder, new_left_post);
iter ++;
vector<int> new_right_inorder(iter, inorder.end());
vector<int> new_right_post(postorder.begin() + new_left.size(), postorder.begin() + sz - 1);
new_root->right = buildTree(new_right_inorder, new_right_post);
return new_root;
}
N o t e : \color{OrangeRed} Note: Note: 构造新的vector的时候,是 左闭右开 \color{OrangeRed} 左闭右开 左闭右开地传值
优化版本:以下代码属于代码随想录
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
// 左闭右开的原则
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};
Leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal
N
o
t
e
:
\color{OrangeRed} Note:
Note:
注意取值:
- preorder[ps]
- idx - is 是 left subtree的inorder vector的size
class Solution {
public:
TreeNode* helper(vector<int>& preorder, vector<int>& inorder, int ps, int pe, int is, int ie)
{
if (ps == pe)
{
return nullptr;
}
TreeNode* root = new TreeNode(preorder[ps]);
if (pe - ps == 1)
{
return root;
}
int idx = 0;
for (int i = is; i < ie; i ++)
{
if (inorder[i] == preorder[ps])
{
idx = i;
break;
}
}
int left_begin_in = is;
int left_end_in = idx;
int right_begin_in = idx + 1;
int right_end_in = ie;
int left_begin_pre = ps + 1;
int left_end_pre = ps + 1 + idx - is;
int right_begin_pre = ps + 1 + idx - is;
int right_end_pre = pe;
root->left = helper(preorder, inorder, left_begin_pre, left_end_pre, left_begin_in, left_end_in);
root->right = helper(preorder, inorder, right_begin_pre, right_end_pre, right_begin_in, right_end_in);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
// in preorder, first is root, second is root->left or root->right
return helper(preorder, inorder, 0, preorder.size(), 0, inorder.size());
}
};
Leetcode 654. Maximum Binary Tree
class Solution {
public:
TreeNode* helper(vector<int>& nums, int s, int e)
{
if (s >= e)
{
return nullptr;
}
int m = INT_MIN;
int idx = s;
for (int i = s; i < e; i ++)
{
if (nums[i] > m)
{
m = nums[i];
idx = i;
}
}
TreeNode* root = new TreeNode(m);
root->left = helper(nums, s, idx);
root->right = helper(nums, idx + 1, e);
return root;
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return helper(nums, 0, nums.size());
}
};