力扣二叉树专题(一)介绍了二叉树的深度优先遍历,前中后序的递归、迭代、统一迭代实现。二叉树还有一种遍历方式就是广度优先遍历,而层序遍历就是应用在二叉树上的广度优先遍历。
层序遍历一个二叉树,就是从左到右一层一层的去遍历二叉树。
栈先进后出适合模拟深度优先遍历,即递归逻辑;队列先进先出,符合一层一层遍历的逻辑,即层序遍历的逻辑。
再次回顾,对于二叉树的遍历总共8种:
深度优先搜索(DFS)看力扣二叉树专题(一),以下是BFS宽度优先搜索(BFS)中层序遍历的C++实现
为了巩固,每个题都用了DFS和BFS实现
这道题提供了层序遍历的模板!后面的九个题都可以套用!
迭代法思路: 先处理根节点,即存入大vector容器result中;再使用队列从左到右访问单层节点,并把当前层的所有节点值存在小vector容器v中。
迭代法做法:
class Solution {
public:
//1.迭代法
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
//1.使用队列遍历节点
queue<TreeNode*> que;
//2.先处理根节点 队列存入根节点
if(root!=nullptr) que.push(root);
//3.再处理下一层节点
while(!que.empty())
{
//4.每一层处理 每一层从左至右依次遍历节点 遍历次数就是队列的大小
int size = que.size();
vector<int> v;//存放每一层的节点值
for(int i=0; i<size ;i++)
{
TreeNode* cur = que.front();//访问队列头部节点
que.pop();
v.push_back(cur->val);//存入当前层的所有节点值
//5.当前层节点处理完,队列元素需要更新为下一层的节点 左到右的顺序
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
//6.当前层的所有节点值存入大容器中
result.push_back(v);
}
return result;
}
};
递归法思路: 单层所有节点依次存入容器中,注意递归法的三个确定。
递归三个确定
递归法做法:
class Solution {
public:
//2.递归
void order(TreeNode* cur, vector<vector<int>>& result, int depth)
{
//1.递归结束 空指针不入队列
if(cur==nullptr) return;
//2.空树情况
if(result.size()==depth) result.push_back(vector<int>());
//3.当前层节点存入容器
result[depth].push_back(cur->val);
//下一层节点 深度要+1 假设每个节点都有两个孩子 当前节点有两个子节点
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;
order(root, result, depth);
return result;
}
};
在题102的基础上,反转数组即可。
递归法
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>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> result;
int depth=0;
order(root, result, depth);
reverse(result.begin(), result.end());
return result;
}
};
迭代法
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
//1.根节点
if(root!=nullptr) que.push(root);
//下一层节点
while(!que.empty())
{
int size = que.size();//遍历次数
vector<int> v;//存放每一层所有节点值
for(int i=0; i<size; i++)
{
TreeNode* cur=que.front();
que.pop();
v.push_back(cur->val);//存放当前层节点
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
result.push_back(v);//存放当前层所有节点
}
reverse(result.begin(), result.end());
return result;
}
};
迭代法: 每次存节点时,保证存放的是当前层最后一个节点即可
class Solution {
public:
//1.迭代法
vector<int> rightSideView(TreeNode* root) {
vector<int> result;
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();
//注意!不能只存放右节点!而是存放右视图看到的节点!
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;
}
};
递归法——搬运sweetiee的思路:
按照 「根结点 -> 右子树 -> 左子树」 的顺序访问,就可以保证每层都是最先访问最右边的节点,获取右视图的节点。
那么左视图就可以按照先序遍历的顺序 「根结点 -> 左子树 -> 右子树」 得到,先序遍历每层最先访问的是最左边的节点。
开始的时候,写的是根节点-左节点-右节点,输出一直是左视图。看到甜姨的思路才恍然大悟!
要注意,不能只存放右节点值,而是存放右视图看到的节点。如果只存放右节点,对于根节点只有一个左孩子的类似情况,无法正确存入。
class Solution {
public:
//递归法
void order(TreeNode* cur, vector<int>& result, int depth)
{
//1.递归结束条件 指针为空
if(cur==nullptr) return;
//3.只存放右视图看到的节点
if(depth==result.size()) result.push_back(cur->val);
depth++;
order(cur->right, result, depth);
order(cur->left, result, depth);
}
vector<int> rightSideView(TreeNode* root) {
vector<int> result;
int depth=0;
order(root, result, depth);
return result;
}
};
思路: 在题107的基础上多了一个求和求均值的步骤,其他一样
迭代法:
class Solution {
public:
//迭代法
vector<double> averageOfLevels(TreeNode* root) {
vector<double> result;
queue<TreeNode*> que;
//先处理根节点
if(root!=nullptr) que.push(root);
while(!que.empty())
{
double 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;
}
};
递归法
class Solution {
public:
//递归法
void order(TreeNode* cur, vector<double>& sum, vector<int>& num, int depth)//3.确定递归传入的参数和返回值
{
//1.确定递归结束条件
if(cur==nullptr) return;
//2.确定递归单次操作 求和 保存单层节点数
//多节点
if(depth<sum.size())
{
sum[depth] += cur->val;
num[depth] += 1;
}
//单节点
else
{
sum.push_back(1.0* cur->val);
num.push_back(1);
}
if(cur->left) order(cur->left, sum, num, depth+1);
if(cur->right) order(cur->right, sum, num, depth+1);
}
vector<double> averageOfLevels(TreeNode* root) {
//求和 节点数
vector<double> sum;
vector<int> num;
int depth=0;
order(root, sum, num, depth);
//求均值
vector<double> result;
int size = sum.size();
for(int i=0;i<size;i++)
{
result.push_back(sum[i]/num[i]);
}
return result;
}
};
N叉树模板!
迭代法: 重点在于孩子节点依次存入队列中
class Solution {
public:
//迭代法
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> result;
queue<Node*> que;
if(root!=NULL) que.push(root);
while(!que.empty())
{
int size=que.size();
vector<int> v;
for(int i=0;i<size;i++)
{
Node* cur=que.front();
que.pop();
v.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(v);
}
return result;
}
};
递归法: 重点在于递归时孩子节点的更新
class Solution {
public:
//递归法
void order(Node* cur, vector<vector<int>>& result, int depth)
{
if(cur==NULL) return;//递归结束条件
if(depth==result.size()) result.push_back(vector<int>());//空树
result[depth].push_back(cur->val);
//更新节点
for(int i=0;i<cur->children.size();i++)
{
order(cur->children[i], result, depth+1);
}
}
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> result;
int depth=0;
order(root, result, depth);
return result;
}
};
迭代法思路: 层序遍历,取每一层的最大值。关键在于,先假设一个最大值,最大值与节点值比较
class Solution {
public:
//迭代法
vector<int> largestValues(TreeNode* root) {
vector<int> result;
queue<TreeNode*> que;
if(root!=nullptr) que.push(root);
while(!que.empty())
{
int size = que.size();
int max = INT_MIN;
for(int i=0;i<size;i++)
{
TreeNode* cur = que.front();
que.pop();
//找最大值
max = max > cur->val? max : cur->val;
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
result.push_back(max);
}
return result;
}
};
递归法思路: 递归法还是传入存放结果的容器、节点指针、树的深度。在找最大值时,通过深度控制,不停地比较容器存放的值与当前节点值。深度与容器大小相等时,说明该层节点都比较完了。
class Solution {
public:
//递归
void order(TreeNode* cur, vector<int>& result, int depth)
{
if(cur==nullptr) return;
//单次操作 找最大值 更新节点
//找最大值时,不停比较 单层节点都比较
if(depth==result.size()) result.push_back(cur->val);
else result[depth] = max(result[depth], cur->val);
order(cur->left, result, depth+1);
order(cur->right, result, depth+1);
}
vector<int> largestValues(TreeNode* root)
{
vector<int> result;
int depth = 0;
order(root, result, depth);
return result;
}
};
迭代法
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
if(root!=NULL) que.push(root);
while(!que.empty())
{
int size=que.size();
Node* cur;
Node* pre;
for(int i=0;i<size;i++)
{
//根节点
if(i==0)
{
pre = que.front();
que.pop();
cur=pre;//记录当前层头节点
}
else
{
cur = que.front();
que.pop();
pre->next=cur;//上一层父节点连向其左子节点;左子节点连向右子节点 本层前一个节点next指向本节点
pre=pre->next;//pre更新为左子节点;pre更新为当前层的下一个节点 节点偏移
}
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
pre->next=NULL;//当前层最后一个节点指向空
}
return root;
}
};
递归法
class Solution {
public:
//递归法
void link(Node* root)
{
if(root==NULL || root->left==NULL) return;//说明遍历结束
root->left->next = root->right;//左节点指向右节点
if(root->next) root->right->next = root->next->left;
link(root->left);
link(root->right);
}
Node* connect(Node* root)
{
link(root);
return root;
}
};
if(root->next) root->right->next = root->next->left;
的解释:
这是个满二叉树,前面已经判断过root->left !=null
才能走到这个逻辑。
所以,root的下一层必然是满的,因此if(root->next)
,也就是说root->next !=null
表明了,root必不是该层的最右节点。
所以root->right、root->right-next、root->next->left都不可能为null,只有root到该层最右节点时,root->right、root->right-next、root->next->left为null,最右节点指向null
和上一题类似,只是这个是二叉树,不是满二叉树。
迭代法: 可以直接套用上一题的代码
class Solution {
public:
Node* connect(Node* root) {
queue<Node*> que;
//1.空指针不入队列
if(root!=NULL) que.push(root);
//2.开始遍历所有节点
while(!que.empty())
{
//3.两个指针 遍历当前层的节点数 一个指针记录当前层头节点 一个用来连接
Node* pre;
Node* cur;
int size = que.size();
//4.遍历当前层节点
for(int i=0; i<size; i++)
{
//5.处理头节点
if(i==0)
{
pre = que.front();
que.pop();
cur = pre;//记录头节点
}
else
{
cur = que.front();//访问要处理的节点
que.pop();
pre->next = cur;//本层上一个节点 连向 本层的节点
pre = pre->next;//pre偏移 更新 让当前节点成为前一个节点
}
//6.更新队列
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
//最右节点指向空
pre->next = NULL;
}
return root;
}
};
递归法:
首先判断当前节点的状态:1.节点为空 2.节点有左右子节点 3.节点只有左子节点 4.节点有右子节点。根据节点的不同状态作不同的处理,最后对函数做递归调用实现对节点左右子树的遍历。具体过程看注释
注意: 一定要先遍历右子树,完成对右子树节点next域的构建,然后再对左子树遍历,才能实现对左子树中孤立左子节点和右子节点next域的构建
class Solution {
public:
//递归法
//1.找到有效节点
Node* findNode(Node* node)
{
if(node==NULL) return NULL;//4.空节点 返回null
else
{
//节点非空
while(node)
{
if(node->left) return node->left;//2.只有左子节点
if(node->right) return node->right;//3.右只有右子节点
node = node->next;//1.节点有左右子节点
}
return node;
}
}
//2.递归
Node* connect(Node* root)
{
//1.如果节点为空,返回null 遍历结束
if(root==NULL) return root;
//如果节点的左右子节点都不为空,三种情况判断
//3.只有右节点非空,遍历当前节点的next域,寻找符合条件的next指向
if(root->right) root->right->next = findNode(root->next);
//4.只有左节点非空 还需要判断右节点是否为空
//右节点不为空 左节点指向右节点;
//右节点为空,就要遍历当前节点的next域,寻找符合条件的next指向 左节点指向符合条件的next指向
if(root->left) root->left->next = root->right ? root->right : findNode(root->next);
//5.递归 先更新右子树节点,再是左子树 不能反
//因为要先把右子树的next域构建好,左子树才能在节点的next域找到正确的next指向。
//如果反着来,会先构建左子树,导致当前节点的next域还未构建好,遗漏掉一些节点的next域,使结果出错
connect(root->right);
connect(root->left);
return root;
}
};
找深度模板!
class Solution {
public:
//1.迭代法
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> V;
if(root!=nullptr) que.push(root);
while(!que.empty())
{
int size = que.size();
vector<int> v;
for(int i=0;i<size;i++)
{
TreeNode* cur = que.front();
que.pop();
v.push_back(cur->val);
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
V.push_back(v);
}
return (int)V.size();
}
};
class Solution {
public:
//2.迭代法
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
int count = 0;
if(root!=nullptr) que.push(root);
while(!que.empty())
{
int size = que.size();
vector<int> v;
for(int i=0;i<size;i++)
{
TreeNode* cur = que.front();
que.pop();
v.push_back(cur->val);
if(cur->left) que.push(cur->left);
if(cur->right) que.push(cur->right);
}
count++;
}
return count;
}
};
class Solution {
public:
//3.迭代法
int maxDepth(TreeNode* root)
{
queue<TreeNode*> que;
int depth=0;
if(root) 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;
}
};
class Solution {
public:
//4.递归法
int maxDepth(TreeNode* root)
{
if(root==nullptr) return 0;
int l = maxDepth(root->left);
int r = maxDepth(root->right);
return max(l,r)+1;
}
};
和上面那题很像,先说递归法,分四种情况
class Solution {
public:
//递归法
int minDepth(TreeNode* root)
{
if(root==nullptr) return 0;
int num=1;
int l=0, r=0;
if(root->left!=nullptr && root->right!=nullptr)
{
l = minDepth(root->left);
r = minDepth(root->right);
num += min(l, r);
}
else if(root->left!=nullptr && root->right==nullptr)
{
num += minDepth(root->left);
}
else num += minDepth(root->right);
return num;
}
};
迭代法:要注意只有左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
class Solution {
public:
//迭代法
int minDepth(TreeNode* root)
{
queue<TreeNode*> que;
if(root==nullptr) return 0;
int depth = 0;
que.push(root);
while(!que.empty())
{
int size = que.size();
depth++;
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);
if(cur->left==nullptr && cur->right==nullptr) return depth;//遍历到最低点
}
}
return depth;
}
};
太好了,这10个题结束了!