二叉树的层序遍历
题目描述
给你二叉树的根节点 root
,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
解题思路
递归实现
沿着二叉树从上到下进行逐层遍历,每到一个新层级就往一个结果集中新增一个列表用于加入当前层的节点值。通过递归调用,它能够访问树的每一个节点,并确保每一层的节点都被集中到一个单独的列表中。这就像是逐层收集树上的果实,并把同一层的果实放在一起。
自定义队列实现
使用一个队列来按顺序访问二叉树的每个节点,同时用一个哈希表来记录每个节点的层级。遍历时,如果碰到一个新层级的节点,就在结果列表中加一个新的子列表。这样,就能一层层地把节点值收集起来,实现了二叉树的逐层遍历。简单来说,就像在排队接水,每个人(节点)按顺序来,按层级分组,确保每一层的人都能按顺序接到水。
原生队列实现
层序遍历符合BFS条件,可以直接使用一个队列来逐层遍历二叉树,每次处理一层的节点,将这些节点的值收集到一个列表中,然后再将这个列表添加到结果列表里。简单来说,就是一层一层地“剥洋葱”,每剥一层就把这层的节点值收集起来。
代码实现
测试地址:https://leetcode.cn/problems/binary-tree-level-order-traversal/
递归实现
class Solution {
public:
void order(TreeNode* cur, vector<vector<int>>& result, int depth)
{
// 如果当前节点为空,则返回
if (cur == nullptr) return;
// 如果结果数组的大小等于当前深度,说明这一层还没有被访问,因此需要新增一个数组
if (result.size() == depth) result.push_back(vector<int>());
// 将当前节点的值加入对应深度的数组中
result[depth].push_back(cur->val);
// 递归遍历左子树,深度加一
order(cur->left, result, depth + 1);
// 递归遍历右子树,深度加一
order(cur->right, result, depth + 1);
}
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
int depth = 0; // 初始深度为0
order(root, result, depth); // 从根节点开始递归遍历
return result;
}
};
自定义队列实现:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans; // 结果向量,用于存储每一层的节点值
if(root != nullptr){ // 如果根节点不为空
queue<TreeNode*> queue; // 创建一个队列,用于BFS遍历
unordered_map<TreeNode*,int> levels; // 创建一个哈希表,用于记录每个节点的层级
queue.push(root); // 将根节点加入队列
levels[root] = 0; // 根节点的层级为0
while(!queue.empty()){ // 当队列不为空时
TreeNode* cur = queue.front(); // 取出队列头部的节点
queue.pop(); // 从队列中移除该节点
int level = levels[cur]; // 获取当前节点的层级
if(ans.size() == level){ // 如果结果向量的大小等于当前层级,说明需要新增一层
ans.push_back(vector<int>()); // 添加一个新的空向量
}
ans[level].push_back(cur->val); // 将当前节点的值加入到对应层级的向量中
if(cur->left != nullptr){ // 如果当前节点有左子节点
queue.push(cur->left); // 将左子节点加入队列
levels[cur->left] = level + 1; // 左子节点的层级为当前层级加1
}
if(cur->right != nullptr){ // 如果当前节点有右子节点
queue.push(cur->right); // 将右子节点加入队列
levels[cur->right] = level + 1; // 右子节点的层级为当前层级加1
}
}
}
return ans; // 返回结果向量
}
};
原生队列实现:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ans; // 结果向量,用于存储每一层的节点值
if (root != nullptr) { // 如果根节点不为空
queue<TreeNode*> queue; // 创建一个队列,用于BFS遍历
queue.push(root); // 将根节点加入队列
while (!queue.empty()) { // 当队列不为空时
int size = queue.size(); // 当前层的节点数量
vector<int> list; // 用于存储当前层的节点值
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
TreeNode* cur = queue.front(); // 取出队列头部的节点
queue.pop(); // 从队列中移除该节点
list.push_back(cur->val); // 将节点值加入当前层的列表
if (cur->left != nullptr) { // 如果当前节点有左子节点
queue.push(cur->left); // 将左子节点加入队列
}
if (cur->right != nullptr) { // 如果当前节点有右子节点
queue.push(cur->right); // 将右子节点加入队列
}
}
ans.push_back(list); // 将当前层的节点值列表加入结果向量
}
}
return ans; // 返回结果向量
}
};
二叉树的层序遍历 II
题目描述
给你二叉树的根节点 root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
解题思路
和层序遍历思路类似,只需要在返回结果集前进行反转即可。
代码实现
测试地址:https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> ans; // 结果向量,用于存储每一层的节点值
if (root != nullptr) { // 如果根节点不为空
queue<TreeNode*> queue; // 创建一个队列,用于BFS遍历
queue.push(root); // 将根节点加入队列
while (!queue.empty()) { // 当队列不为空时
int size = queue.size(); // 当前层的节点数量
vector<int> list; // 用于存储当前层的节点值
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
TreeNode* cur = queue.front(); // 取出队列头部的节点
queue.pop(); // 从队列中移除该节点
list.push_back(cur->val); // 将节点值加入当前层的列表
if (cur->left ) { // 如果当前节点有左子节点
queue.push(cur->left); // 将左子节点加入队列
}
if (cur->right) { // 如果当前节点有右子节点
queue.push(cur->right); // 将右子节点加入队列
}
}
ans.push_back(list); // 将当前层的节点值列表加入结果向量
}
}
reverse(ans.begin(), ans.end()); //反转结果集
return ans; // 返回结果向量
}
};
二叉树的右视图
题目描述
给定一个二叉树的 根节点 root
,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
解题思路
在层序遍历的过程中,判断当前元素是否为单层的最后一个元素,如果是的话,将其加入结果集中
代码实现
测试地址:https://leetcode.cn/problems/binary-tree-right-side-view/
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> que; // 创建一个队列,用于层序遍历二叉树
if (root != nullptr) { // 如果根节点不为空
que.push(root); // 将根节点加入队列
}
vector<int> ans; // 结果向量,用于存储右视图的节点值
while (!que.empty()) { // 当队列不为空时
int size = que.size(); // 获取当前层的节点数量
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
TreeNode* node = que.front(); // 取出队列头部的节点
que.pop(); // 从队列中移除该节点
if (i == (size - 1)) // 如果当前节点是当前层的最后一个节点
ans.push_back(node->val); // 将该节点的值加入结果向量
if (node->left) // 如果当前节点有左子节点
que.push(node->left); // 将左子节点加入队列
if (node->right) // 如果当前节点有右子节点
que.push(node->right); // 将右子节点加入队列
}
}
return ans; // 返回结果向量
}
};
二叉树的层平均值
题目描述
给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。
解题思路
在层序遍历的时候,将每层元素的总和加入到结果集中
代码实现
测试地址:https://leetcode.cn/problems/average-of-levels-in-binary-tree/
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> que; // 创建一个队列,用于层序遍历二叉树
vector<double> ans; // 结果向量,用于存储每层的平均值
if (root != nullptr) // 如果根节点不为空
que.push(root); // 将根节点加入队列
while (!que.empty()) { // 当队列不为空时
int size = que.size(); // 获取当前层的节点数量
double sum = 0; // 用于计算当前层节点值的总和
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
TreeNode* node = que.front(); // 取出队列头部的节点
que.pop(); // 从队列中移除该节点
sum += node->val; // 将节点值加入总和
if (node->left) // 如果当前节点有左子节点
que.push(node->left); // 将左子节点加入队列
if (node->right) // 如果当前节点有右子节点
que.push(node->right); // 将右子节点加入队列
}
ans.push_back(sum / size); // 将当前层的平均值加入结果向量
}
return ans; // 返回结果向量
}
};
N叉树的层序遍历
题目描述
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
例如,给定一个 3叉树 :
解题思路
和之前的思路差不多,只是一个节点有多个孩子,遍历加入队列即可。
代码实现
测试地址:https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node*> que; // 创建一个队列,用于层序遍历N叉树
vector<vector<int>> ans; // 结果向量,用于存储层序遍历的每层节点值
if (root != nullptr) // 如果根节点不为空
que.push(root); // 将根节点加入队列
while (!que.empty()) { // 当队列不为空时
int size = que.size(); // 获取当前层的节点数量
vector<int> vec; // 用于存储当前层的节点值
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
Node* node = que.front(); // 取出队列头部的节点
que.pop(); // 从队列中移除该节点
vec.push_back(node->val); // 将节点值加入当前层的节点值向量
// 遍历当前节点的所有子节点,并将它们加入队列
for (int i = 0; i < node->children.size(); i++) {
if (node->children[i]) {
que.push(node->children[i]);
}
}
}
ans.push_back(vec); // 将当前层的节点值向量加入结果向量
}
return ans; // 返回结果向量
}
};
在每个树行中找最大值
题目描述
您需要在二叉树的每一行中找到最大的值。
解题思路
层序遍历取每层最大值加入到结果集返回即可。
代码实现
测试地址:https://leetcode.cn/problems/find-largest-value-in-each-tree-row/
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> que; // 创建一个队列,用于层序遍历
vector<int> ans; // 结果向量,用于存储每层的最大值
if (root != nullptr) // 如果根节点不为空
que.push(root); // 将根节点加入队列
while (!que.empty()) { // 当队列不为空时
int size = que.size(); // 获取当前层的节点数量
int tmp = INT_MIN; // 用于记录当前层的最大值,初始化为INT_MIN
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
TreeNode* cur = que.front(); // 取出队列头部的节点
que.pop(); // 从队列中移除该节点
tmp = max(tmp, cur->val); // 更新当前层的最大值
if (cur->left) { // 如果当前节点有左子节点
que.push(cur->left); // 将左子节点加入队列,以便后续处理
}
if (cur->right) { // 如果当前节点有右子节点
que.push(cur->right); // 将右子节点加入队列
}
}
ans.push_back(tmp); // 将当前层的最大值加入结果向量
}
return ans; // 返回结果向量
}
};
填充每个节点的下一个右侧节点指针
题目描述
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
解题思路
通过层序遍历的方式,逐层连接树中的各个节点。在每一层中,通过一个队列来管理节点,并使用两个指针 preNode
和 node
来跟踪当前正在处理的节点和前一个节点。对于每一层的节点,首先处理第一个节点,然后依次处理后续节点,将前一个节点的 next
指针指向当前节点,实现节点间的连接。每层最后一个节点的 next
指针默认指向 NULL
。
代码实现
测试地址:
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que; // 使用队列进行层序遍历
if (root != nullptr) // 如果根节点不为空,则将其加入队列
que.push(root);
while (!que.empty()) { // 当队列不为空时,进行遍历
int size = que.size(); // 获取当前层的节点数量
Node* node; // 用于指向当前处理的节点
Node* preNode; // 用于指向前一个节点,以便连接next指针
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
if (i == 0) { // 如果是当前层的第一个节点
preNode = que.front(); // 获取队列的第一个节点作为前一个节点
que.pop(); // 从队列中移除已处理的节点
node = preNode; // 初始化当前节点为第一个节点
} else { // 如果不是当前层的第一个节点
node = que.front(); // 更新当前节点为队列的第一个节点
que.pop(); // 从队列中移除已处理的节点
preNode->next = node; // 将前一个节点的next指针指向当前节点,实现连接
preNode = preNode->next; // 将前一个节点更新为当前节点,为下一次连接做准备
}
// 将当前节点的左右子节点加入队列,为下一层的遍历做准备
if (node->left) {
que.push(node->left);
}
if (node->right) {
que.push(node->right);
}
}
//本层最后一个节点指向NULL
preNode->next = NULL;
}
return root;
}
};
填充每个节点的下一个右侧节点指针 II
题目描述
给定一个二叉树:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL
。
初始状态下,所有 next 指针都被设置为 NULL
。
解题思路
和上题没啥区别,无非是从完整二叉树变为了普通二叉树,代码和上题一样。
代码实现
测试地址:
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que; // 使用队列进行层序遍历
if (root != nullptr) // 如果根节点不为空,则将其加入队列
que.push(root);
while (!que.empty()) { // 当队列不为空时,进行遍历
int size = que.size(); // 获取当前层的节点数量
Node* node; // 用于指向当前处理的节点
Node* preNode; // 用于指向前一个节点,以便连接next指针
for (int i = 0; i < size; i++) { // 遍历当前层的所有节点
if (i == 0) { // 如果是当前层的第一个节点
preNode = que.front(); // 获取队列的第一个节点作为前一个节点
que.pop(); // 从队列中移除已处理的节点
node = preNode; // 初始化当前节点为第一个节点
} else { // 如果不是当前层的第一个节点
node = que.front(); // 更新当前节点为队列的第一个节点
que.pop(); // 从队列中移除已处理的节点
preNode->next =
node; // 将前一个节点的next指针指向当前节点,实现连接
preNode =
preNode
->next; // 将前一个节点更新为当前节点,为下一次连接做准备
}
// 将当前节点的左右子节点加入队列,为下一层的遍历做准备
if (node->left) {
que.push(node->left);
}
if (node->right) {
que.push(node->right);
}
}
// 本层最后一个节点指向NULL
preNode->next = NULL;
}
return root;
}
};
二叉树的最大深度
题目描述
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
返回它的最大深度 3 。
解题思路
递归版
通过递归地计算左子树和右子树的深度,然后取两者中的最大值并加1(表示当前节点),从而得到以当前节点为根的树的最大深度。
迭代版
在进行层序遍历的时候记录遍历的次数,即为树的最大深度。
代码实现
测试地址:https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/
递归版实现
class Solution {
public:
int maxDepth(TreeNode* root) {
return root == nullptr
? 0
: max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
迭代版实现
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr)
return 0;
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int size = que.size();
depth++; // 记录深度
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 depth;
}
};
二叉树的最小深度
题目描述
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明: 叶子节点是指没有子节点的节点。
解题思路
迭代版
使用层序遍历的方式,当左右孩子都为空时,说明已经遍历到最低点了,如果其中一个孩子为空,说明还能够继续遍历,不是最低点。
递归版
过递归地计算左子树和右子树的最小深度,然后取两者中的最小值并加1(表示当前节点),从而得到以当前节点为根的树的最小深度。
代码实现
测试地址:https://leetcode.cn/problems/minimum-depth-of-binary-tree/
迭代版实现
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
int size = que.size();
depth++; // 记录最小深度
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);
if (!node->left && !node->right) { // 当左右孩子都为空的时候,说明是最低点的一层了,退出
return depth;
}
}
}
return depth;
}
};
递归实现
class Solution {
public:
int minDepth(TreeNode* root) {
// 如果树为空,返回深度0
if (root == nullptr)
return 0;
// 如果当前节点是叶节点,返回深度1
if (root->left == nullptr && root->right == nullptr)
return 1;
// 初始化左子树和右子树的最小深度为最大值
int ldeep = INT_MAX;
int rdeep = INT_MAX;
// 如果左子树存在,计算其最小深度
if (root->left != nullptr) {
ldeep = minDepth(root->left);
}
// 如果右子树存在,计算其最小深度
if (root->right != nullptr) {
rdeep = minDepth(root->right);
}
// 返回左右子树中深度较小的那个,并加1(当前节点)
return min(ldeep, rdeep) + 1;
}
};