# 完全二叉树的节点个数
题目链接🔗
# 普通二叉树思路
# 递归
class Solution { | |
public: | |
int countNodes(TreeNode* root) { | |
if (root == nullptr) return 0; | |
return 1 + countNodes(root->left) + countNodes(root->right); | |
} | |
}; |
# 迭代
class Solution { | |
public: | |
int countNodes(TreeNode* root) { | |
if(root == nullptr) return 0; | |
queue<TreeNode*> que; | |
que.push(root); | |
int count = 0; | |
while(!que.empty()) { | |
int size = que.size(); | |
count += size; | |
for(int i = 0; i < size; i++) { | |
TreeNode* node = que.front(); | |
que.pop(); | |
if(node->left) que.push(node->left); | |
if(node->right) que.push(node->right); | |
} | |
} | |
return count; | |
} | |
}; |
# 完全二叉树思路
在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 层,则该层包含 个节点。
完全二叉树只有两种情况:
- 满二叉树
- 可以直接用 来计算。根节点深度为 1
- 最后一层叶子节点没有满
- 分别递归左右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后按照情况 1 来计算。
那如何判断左子树或者右子树是满二叉树?
在完全二叉树中,如果递归向左遍历的深度等于递归向右的深度,就是满二叉树。
那本题可以利用这个公式来计算满二叉树的节点数量,如果不是满二叉树则递归。
class Soulution { | |
public: | |
int countNodes(TreeNode* root) { | |
if(root == nullptr) return 0; | |
TreeNode* left = root->left; | |
TreeNode* right = root->right; | |
int leftDepth = 0, rightDepth = 0; | |
while(left) { | |
left = left->left; | |
leftDepth++; | |
} | |
while(right) { | |
right = right->right; | |
rightDepth++; | |
} | |
if(leftDepth == rightDepth) { | |
return (2 << leftDepth) - 1; // 注意 (2<<1) 相当于 2^2,所以 leftDepth 初始为 0 | |
} | |
return countNodes(root->left) + countNodes(root->right) + 1; | |
} | |
}; |
# 平衡二叉树
题目链接🔗
因为求深度可以从上到下去查 所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)。
- 确定递归函数的参数和返回值
- 参数:当前传入的节点
- 返回值: 以当前传入节点为根节点树的高度
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回 - 1 来标记已经不符合平衡树的规则了。
- 明确终止条件
递归的过程中依然是遇到空节点了为终止,返回 0,表示当前节点为根节点的树高度为 0
- 确定单层递归逻辑
分别求出其左右子树的高度,如果差值小于等于 1,则返回当前二叉树的高度,否则返回 - 1,表示已经不是二叉平衡树了。
class Solution { | |
public: | |
// 返回以该节点为根节点的二叉树的高度,如果不是平衡二叉树了则返回 - 1 | |
int getHeight(TreeNode* node) { | |
if (node == nullptr) { | |
return 0; | |
} | |
int leftHeight = getHeight(node->left); | |
if (leftHeight == -1) return -1; | |
int rightHeight = getHeight(node->right); | |
if (rightHeight == -1) return -1; | |
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight); | |
} | |
bool isBalanced(TreeNode* root) { | |
return getHeight(root) == -1 ? false : true; | |
} | |
}; |
# 二叉树的所有路径
题目链接🔗
题目要求从根节点到叶子节点的路径,所以要前序遍历
因为我们要把路径记录下来,还需要回溯来回退一个路径再进入另一个路径。
- 递归函数的参数和返回值
要传入根节点,记录每一条路径的 path,和存放结果集的 result,这里递归不需要返回值。
- 确定递归终止条件
当 cur 不为空,其左右孩子都为空的时候,就找到叶子节点
- 确定单层递归逻辑
因为是前序遍历要先处理中间节点。将中间节点放进 path 中,然后是递归和回溯的过程。
class Solution { | |
private: | |
void traversal(TreeNode* cur, string path, vector<string>& result) { | |
path += to_string(cur->val); // 中 | |
// 叶子节点 | |
if (cur->left == nullptr && cur->right == nullptr) { | |
result.push_back(path); | |
return; | |
} | |
if (cur->left) traversal(cur->left, path + "->", result); // 左 | |
if (cur->right) traversal(cur->right, path + "->", result); // 右 | |
} | |
public: | |
vector<string> binaryTreePaths(TreeNode* root) { | |
vector<string> result; | |
string path; | |
if (root == nullptr) return result; | |
traversal(root, path, result); | |
return result; | |
} | |
}; |
注意在函数定义的时候 void traversal(TreeNode* cur, string path, vector<string>& result)
,定义的是 string path
,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。
回溯就隐藏在 traversal(cur->left, path + "->", result);
中的 path + "->"
,因为并没有改变 path 的数值,执行完递归函数之后,path 依然是之前的数值(相当于回溯了)。
# 左子叶之和
题目链接🔗
左叶子的定义:节点 A 的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么 A 节点的左孩子为左叶子节点。
要注意是判断左叶子,不是二叉树左侧节点,所以不能层序遍历。
那么判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。
如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子。
# 递归
- 确定递归函数的参数和返回值
判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和。
- 确定终止条件
如果是空节点则左子叶值为 0.
并且,只有当前遍历的节点是父节点,才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是 0。
- 确定单层递归逻辑
当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。
class Solution { | |
public: | |
int sumOfLeftLeaves(TreeNode* root) { | |
if (root == nullptr) return 0; | |
if (root->left == nullptr && root->right== nullptr) return 0; | |
int leftValue = sumOfLeftLeaves(root->left); // 左 | |
if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况 | |
leftValue = root->left->val; | |
} | |
int rightValue = sumOfLeftLeaves(root->right); // 右 | |
int sum = leftValue + rightValue; // 中 | |
return sum; | |
} | |
}; | |
/* 精简代码 */ | |
class Solution { | |
public: | |
int sumOfLeftLeaves(TreeNode* root) { | |
if (root == nullptr) return 0; | |
int leftValue = 0; | |
if (root->left != nullptr && root->left->left == nullptr && root->left->right == nullptr) { | |
leftValue = root->left->val; | |
} | |
return leftValue + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right); | |
} | |
}; |
# 迭代
迭代前中后序都可以,以前序为例。
class Solution { | |
public: | |
int sumOfLeftLeaves(TreeNode* root) { | |
stack<TreeNode*> st; | |
if (root == NULL) return 0; | |
st.push(root); | |
int result = 0; | |
while (!st.empty()) { | |
TreeNode* node = st.top(); | |
st.pop(); | |
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) { | |
result += node->left->val; | |
} | |
if (node->right) st.push(node->right); | |
if (node->left) st.push(node->left); | |
} | |
return result; | |
} | |
}; |