二叉树层序遍历解决的一系列问题
Leetcode 102.二叉树的层序遍历
题目链接:102 二叉树的层序遍历
题干:给你二叉树的根节点
root
,返回其节点值的 层序遍历。(即逐层地,从左到右访问所有节点)。
思考: 借用一个辅助队列来实现。每次循环记录当前队列元素个数(即树上一层节点个数),循环出栈并写入数组vec,同时将其非空孩子节点入队,最后将数组vec写入数组result。
代码:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
vec.push_back(cur->val);
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
result.push_back(vec);
}
return result;
}
};
Leetcode 107.二叉树的层次遍历 II
题目链接:107 二叉树的层次遍历 II
题干:给你二叉树的根节点
root
,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思考: 按正常的层次遍历后反转下数组即可。或者先将元素存入栈中,每层元素末尾入栈一个null做标志,最后将栈内元素间隔null输出也可。(此处给出最后反转形式)
代码:
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
vector<int> vec;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
vec.push_back(cur->val);
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
result.push_back(vec);
}
reverse(result.begin(),result.end());
return result;
}
};
Leetcode 199.二叉树的右视图
题目链接:199 二叉树的右视图
题干:给定一个二叉树的 根节点
root
,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。
思考: 层序遍历的时候,判断是否遍历到单层的最后面的元素,如果是则放进result数组中
代码:
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
queue<TreeNode*> que;
vector<int> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (i == size - 1) result.push_back(cur->val); //只存储每层最后一个元素
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return result;
}
};
Leetcode 637.二叉树的层平均值
题目链接:637 二叉树的层平均值
题干:给定一个非空二叉树的根节点
root
, 以数组的形式返回每一层节点的平均值。与实际答案相差10-5
以内的答案可以被接受。
思考: 层序遍历时,把一层求个总和再取一个均值存入数组即可
代码:
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
queue<TreeNode*> que;
vector<double> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
double sum = 0; //记录每层节点值的总和
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
sum += cur->val;
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
result.push_back(sum / size);
}
return result;
}
};
Leetcode 429.N叉树的层序遍历
题目链接:429 N叉树的层序遍历
题干:给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。树的序列化输入是用层序遍历,每组子节点都由 null 值分隔。
思考: 和二叉树的层序遍历唯一的区别在于孩子个数,只要循环中将多个非空孩子节点入队即可
代码:
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
queue<Node*> que;
vector<vector<int>> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
vector<int> vec;
for (int i = 0; i < size; i++) {
Node* cur = que.front();
que.pop();
vec.push_back(cur->val);
//将当前节点的所有非空孩子节点入队
for (int j = 0; j < cur->children.size(); j++)
if (cur->children[j]) que.push(cur->children[j]);
}
result.push_back(vec);
}
return result;
}
};
Leetcode 515.在每个树行中找最大值
题目链接:515 在每个树行中找最大值
题干:给定一棵二叉树的根节点
root
,请找出该二叉树中每一层的最大值。
思考: 层序遍历,取每一层的最大值
代码:
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
queue<TreeNode*> que;
vector<int> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
int max = que.front()->val; //记录本层最大值
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (cur->val > max) max = cur->val;
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
result.push_back(max);
}
return result;
}
};
Leetcode 116.填充每个节点的下一个右侧节点指针
题目链接:116 填充每个节点的下一个右侧节点指针
题干:给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node { int val; Node *left; Node *right; Node *next; }
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为
NULL
。初始状态下,所有 next 指针都被设置为NULL
。
思考: 层序遍历,在单层遍历时除本层末尾节点next指向null,其余节点next指向队头节点
代码:
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
for (int i = 0; i < size; i++) {
Node* cur = que.front();
que.pop();
if (i != size - 1)
cur->next = que.front();
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return root;
}
};
Leetcode 117.填充每个节点的下一个右侧节点指针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(); //记录每层节点个数
for (int i = 0; i < size; i++) {
Node* cur = que.front();
que.pop();
if (i != size - 1)
cur->next = que.front();
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return root;
}
};
Leetcode 104.二叉树的最大深度
题目链接:104 二叉树的最大深度
题干:给定一个二叉树root,返回其最大深度。二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。
思考:层序遍历,每层将记录值depth增一
代码:
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
int depth = 0;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
depth++;
}
return depth;
}
};
Leetcode 111.二叉树的最小深度
题目链接:111 二叉树的最小深度
题干:给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。叶子节点是指没有子节点的节点。
思考: 层序遍历,单层循环时添加一个判断,当左右孩子都为空时说明遍历到最低点,返回深度
代码:
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
int depth = 0;
if (root != nullptr) que.push(root);
while (!que.empty()) {
depth++;
int size = que.size(); //记录每层节点个数
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
if (!cur->left && !cur->right)
return depth;
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return depth;
}
};
Leetcode 226.翻转二叉树
题目链接:226 翻转二叉树
题干:给你一棵二叉树的根节点 `root` ,翻转这棵二叉树,并返回其根节点。
思考:把每一个节点的左右孩子交换一下即可。此处使用前序遍历、后序遍历以及层序遍历都可以,唯独中序遍历比较特殊。中序遍历递归遍历变化:左孩子先处理,再处理根节点,最后要处理的右孩子已经变成了左孩子,所以是处理新的左孩子。(此处给出先序遍历和层序遍历)
代码一(递归法):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
代码二(迭代法 深度优先遍历):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) return root;
stack<TreeNode*> st;
st.push(root); //中
while (!st.empty()) {
TreeNode* cur = st.top();
st.pop();
swap(cur->left, cur->right);
if (cur->right != nullptr) st.push(cur->right); //右
if (cur->left != nullptr) st.push(cur->left); //左
}
return root;
}
};
代码三(统一迭代法 深度优先遍历):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if (root == nullptr) return root;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
if (cur != nullptr) {
st.pop();
if (cur->right != nullptr) st.push(cur->right); //右
if (cur->left != nullptr) st.push(cur->left); //左
st.push(cur); //中
st.push(nullptr); //null标志 说明后面的节点访问过
} else {
st.pop();
cur = st.top();
st.pop();
swap(cur->left, cur->right); //统一处理
}
}
return root;
}
};
代码四(层序遍历 广度优先遍历):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size(); //记录每层节点个数
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
swap(cur->left, cur->right);
//将左右非空孩子节点加入队列
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return root;
}
};
Leetcode 101. 对称二叉树
题目链接:101 对称二叉树
题干:给你一个二叉树的根节点 root , 检查它是否轴对称
思考一:二叉树是否对称其实要比较的是两个树(这两个树是根节点的左右子树)。比较的是两个子树的里侧和外侧的元素是否相等。
首先要把两个节点为空的情况弄清楚
- 左节点为空,右节点不为空,不对称,return false
- 左不为空,右为空,不对称 return false
- 左右都为空,对称,返回true
剩下的就是左右节点不为空:
- 左右都不为空,比较节点数值,不相同就return false
接着确定单层递归的逻辑 :
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
代码:
class Solution {
public:
bool compare(TreeNode* left, TreeNode* right) {
//终止条件
if (left && !right) return false;
else if (!left && right) return false;
else if (!left && !right) return true;
else if (left->val != right->val) return false;
//单层循环
bool outside = compare(left->left, right->right); //外层比较
bool inside = compare(left->right, right->left); //内层比较
bool result = outside && inside;
return result;
}
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return compare(root->left, root->right);
}
};
思考二:通过队列或栈来判断根节点的左子树和右子树的内侧和外侧是否相等。其实是把左右两个子树要比较的元素顺序放进一个容器,然后成对成对的取出来进行比较。(此处给出队列的)
代码:
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
queue<TreeNode*> que;
que.push(root->left);
que.push(root->right);
while (!que.empty()) {
TreeNode* leftNode = que.front();
que.pop();
TreeNode* rightNode = que.front();
que.pop();
if (!leftNode && !rightNode) //左右节点均为空则对称
continue;
//若左右孩子仅一个为空或左右孩子的值不相等则不对称
if (!leftNode || !rightNode || leftNode->val != rightNode->val)
return false;
que.push(leftNode->left);
que.push(rightNode->right);
que.push(leftNode->right);
que.push(rightNode->left);
}
return true;
}
};
自我总结:
- 熟悉层序遍历
- 掌握递归法的精髓:
- 确定递归函数的参数和返回
- 确定终止条件
- 确定单层递归的逻辑