目录
一、做题心得
今天是代码随想录打卡的第15天,学习的是二叉树部分的part3。主要也是递归思想和BFS,DFS遍历的应用,总体而言难度不大,但是递归仍然不容易想到如何实现。今天的题也分别提到了完全二叉树,平衡二叉树以及叶子节点等知识点,恰好也作为巩固来练习。
话不多说,直接开始今天的内容。
二、题目与题解
题目一:222.完全二叉树的节点个数
题目链接
222. 完全二叉树的节点个数 - 力扣(LeetCode)
给你一棵 完全二叉树 的根节点
root
,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第
h
层,则该层包含1~ 2h
个节点。示例 1:
输入:root = [1,2,3,4,5,6] 输出:6示例 2:
输入:root = [] 输出:0示例 3:
输入:root = [1] 输出:1提示:
- 树中节点的数目范围是
[0, 5 * 104]
0 <= Node.val <= 5 * 104
- 题目数据保证输入的树是 完全二叉树
题解1:递归
这个题我们很容易想到直接递归的方式,就当做一个普通二叉树来看,直接得到一颗任意二叉树的节点数,方法上讲和昨天打卡的二叉树的最大深度差不多。
二叉树节点数 = 左子树节点数 + 右子树节点数 + 1(即根节点)
代码如下:
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
return countNodes(root->left) + countNodes(root->right) + 1; //左子树节点数+右子树节点数+1(根节点)
}
};
题解2:BFS(直接套模板)
前两天其实就做了不少用BFS进行层序遍历从而解决二叉树问题的了,这里就不做过多解释,直接套用之前层序遍历的模板,挨个遍历树的全部节点。
代码如下:
class Solution {
public:
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*> q;
int ans = 0;
q.push(root);
while (!q.empty()) {
int size = q.size();
ans += size;
for (int i = 0; i < size; i++) {
TreeNode* node = q.front();
q.pop();
if(node->left) q.push(node->left);
if(node->right) q.push(node->right);
}
}
return ans;
}
};
这道题其实还有二分的解法,相对而言更高效,这里我主要想说的是对于任意一颗二叉树的通法,所有这种方法就不过多介绍了。
题目二:110. 平衡二叉树
题目链接
给定一个二叉树,判断它是否是
平衡二叉树
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false示例 3:
输入:root = [] 输出:true提示:
- 树中的节点数在范围
[0, 5000]
内-104 <= Node.val <= 104
题解:递归(DFS)
首先说下平衡二叉树的定义:
平衡二叉树是指该树所有节点的左右子树的深度相差不超过1的二叉树。(注意是所有节点)
然后我们看到这道题,判断所有节点的左右子树,自然而然就容易想到递归的解法:
递归:确保所有节点左右子树的深度相差不超过1(绝对值之差小于等于1)
确保根节点满足:abs(Depth(root->left) - Depth(root->right)) <= 1
所有节点都满足:体现在递归的实现,每次递归都会往左右子树向下遍历(isBalanced(root->left) && isBalanced(root->right))
代码如下:
class Solution {
public:
int Depth(TreeNode* node) { //计算某节点子树最大深度
if (node == nullptr) return 0;
return max(Depth(node->left),Depth(node->right)) + 1;
}
bool isBalanced(TreeNode* root) {
if (root == nullptr) return true;
return abs(Depth(root->left) - Depth(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
};
题目三:257. 二叉树的所有路径
题目链接
给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5] 输出:["1->2->5","1->3"]示例 2:
输入:root = [1] 输出:["1"]提示:
- 树中节点的数目在范围
[1, 100]
内-100 <= Node.val <= 100
题解:DFS前序遍历(递归)
这里用到一个函数:
to_string():将数字转化为字符串
这个题要得到二叉树的所有路径,首先就该想到的便是递归思想。
定义traversal()函数来实现添加全部路径:
1.首先将当前节点的值转换为字符串并追加到path
中。
2.然后,它检查当前节点是否为叶子节点(即没有左右子节点)。
如果是叶子节点,说明找到了一条从根到叶子的完整路径,于是将该路径添加到ans
中。
如果当前节点不是叶子节点,则递归地对左子节点和右子节点调用traversal
函数,同时在path
后添加"->"作为路径中节点之间的分隔符。
class Solution {
public:
void traversal(TreeNode* cur, string path, vector<string>& ans) {
path += to_string(cur->val); //将当前节点的值转换为字符串并添加到当前路径的末尾 //根
if (cur->left == nullptr && cur->right == nullptr) { //递归终止条件:当前节点没有子节点(此时就是叶子节点)
ans.push_back(path); //添加路径到结果中
return ;
}
if (cur->left) traversal(cur->left, path + "->",ans); //如果存在左子节点,递归遍历左子树,并在当前路径后添加"->"作为分隔符
if (cur->right) traversal(cur->right, path + "->",ans); //如果存在右子节点,递归遍历右子树,同样在当前路径后添加"->"作为分隔符
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> ans; //存放结果(路径)
string path; //路径
if (root == nullptr) return ans;
traversal(root, path, ans);
return ans;
}
};
题目四:404. 左叶子之和
题目链接
给定二叉树的根节点
root
,返回所有左叶子之和。示例 1:
输入: root = [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24示例 2:
输入: root = [1] 输出: 0提示:
- 节点数在
[1, 1000]
范围内-1000 <= Node.val <= 1000
题解:BFS(套模板)
这道题也可以作为一个BFS层序遍历题来做,可以直接套之前用的层序遍历模板:【代码随想录训练营第42期 Day13打卡 二叉树的遍历-- LeetCode 144. 二叉树的前序遍历 94. 二叉树的中序遍历 145. 二叉树的后序遍历 102. 二叉树的层序遍历-CSDN博客
这里要注意的就是定义函数IsLeaf(),用来判断当前节点是否为叶子节点(左右节点都没有)。
然后就是通过层序遍历逐个遍历找到左叶子节点即可。
代码如下:
class Solution {
public:
bool IsLeaf(TreeNode* node) { //定义IsLeaf()函数判断某节点是否为叶子节点(左右子节点都没有)
return !node->left && !node->right;
}
int sumOfLeftLeaves(TreeNode* root) {
queue<TreeNode*> q;
int ans = 0;
if (root == nullptr) return 0;
q.push(root);
while (!q.empty()) { //经典模板
TreeNode* node = q.front();
q.pop();
if (node->left) {
if (IsLeaf(node->left)) { //(当前节点的)左子节点为叶子节点(即没子节点的节点)
ans += node->left->val;
}
else {
q.push(node->left); //如果左子节点不是叶子节点,则将其加入队列以便后续处理
}
}
if (node->right) {
if (!IsLeaf(node->right)) { //node->right有子节点时
q.push(node->right); //将其加入队列以便后续处理
}
}
}
return ans;
}
};
三、小结
今天的题目整体难度不大,但帮助我们对于递归的理解还是很重要。个人感觉自己对递归还是理解的比较浅,后边还会继续学习回溯在递归中的应用与作用。好了,今天的打卡到此结束。最后,我是算法小白,但也希望终有所获。