学会二叉树的层序遍历,可以一口气打完以下十题:
102.二叉树的层序遍历
107.二叉树的层次遍历II
199.二叉树的右视图
637.二叉树的层平均值
429.N叉树的层序遍历
515.在每个树行中找最大值
116.填充每个节点的下一个右侧节点指针
117.填充每个节点的下一个右侧节点指针II
104.二叉树的最大深度
111.二叉树的最小深度
力扣题目链接
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
我们之前讲过了三篇关于二叉树的深度优先遍历的文章:
二叉树:前中后序递归法
二叉树:前中后序迭代法
二叉树:前中后序迭代方式统一写法
接下来我们再来介绍二叉树的另一种遍历方式:层序遍历。
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。
而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
使用队列实现二叉树广度优先遍历,动画如下:
这样就实现了层序从左到右遍历二叉树。
代码如下:这份代码也可以作为二叉树层序遍历的模板,打十个就靠它了。
c++代码如下:
class Solution { public: vector> levelOrder(TreeNode* root) { queue que; if (root != NULL) que.push(root); vector > result; while (!que.empty()) { int size = que.size(); vector vec; // 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的 for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); vec.push_back(node->val); if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(vec); } return result; } };
# 递归法 class Solution { public: void order(TreeNode* cur, vector>& result, int depth) { if (cur == nullptr) return; if (result.size() == depth) result.push_back(vector ()); result[depth].push_back(cur->val); order(cur->left, result, depth + 1); order(cur->right, result, depth + 1); } vector > levelOrder(TreeNode* root) { vector > result; int depth = 0; order(root, result, depth); return result; } };
class Solution { public: void order(TreeNode* root,vector> &vec,int depth){ if(root==nullptr) return; if(depth == vec.size()) vec.push_back(vector ()); vec[depth].push_back(root->val); order(root->left,vec,depth+1); order(root->right,vec,depth+1); } vector rightSideView(TreeNode* root) { vector > result; int depth = 0; order(root,result,depth); vector result1; for(int i=0;i rightSideView(TreeNode* root) { queue que; if(root!=nullptr) que.push(root); vector vec; while(!que.empty()){ int size = que.size(); for(int i=0;i val); } if(node->left) que.push(node->left); if(node->right) que.push(node->right); } } return vec; } };
class Solution { public: vectoraverageOfLevels(TreeNode* root) { queue que; vector result; if(root!=nullptr) que.push(root); while(!que.empty()){ int size = que.size(); long aver = 0; for(int i=0;i val; if(node->left) que.push(node->left); if(node->right) que.push(node->right); } result.push_back(aver*1.0/size); } return result; } };
力扣题目链接
给定一个 N 叉树,返回其节点值的层序遍历。 (即从左到右,逐层遍历)。
例如,给定一个 3叉树 :
返回其层序遍历:
[ [1], [3,2,4], [5,6] ]
这道题依旧是模板题,只不过一个节点有多个孩子了
C++代码:
class Solution { public: vector> levelOrder(Node* root) { queue que; if (root != NULL) que.push(root); vector > result; while (!que.empty()) { int size = que.size(); vector 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]); } } result.push_back(vec); } return result; } };
力扣题目链接
您需要在二叉树的每一行中找到最大的值。
层序遍历,取每一层的最大值
C++代码:
class Solution { public: vectorlargestValues(TreeNode* root) { queue que; if (root != NULL) que.push(root); vector result; while (!que.empty()) { int size = que.size(); int maxValue = INT_MIN; // 取每一层的最大值 for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); maxValue = node->val > maxValue ? node->val : maxValue; if (node->left) que.push(node->left); if (node->right) que.push(node->right); } result.push_back(maxValue); // 把最大值放进数组 } return result; } };
力扣题目链接(opens new window)
给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node { int val; Node *left; Node *right; Node *next; }
1 2 3 4 5 6
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。
本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了
C++代码:
class Solution { public: Node* connect(Node* root) { queueque; if (root != NULL) que.push(root); while (!que.empty()) { int size = que.size(); // vector vec; Node* nodePre; Node* node; for (int i = 0; i < size; i++) { if (i == 0) { nodePre = que.front(); // 取出一层的头结点 que.pop(); node = nodePre; } else { node = que.front(); que.pop(); nodePre->next = node; // 本层前一个节点next指向本节点 nodePre = nodePre->next; } if (node->left) que.push(node->left); if (node->right) que.push(node->right); } nodePre->next = NULL; // 本层最后一个节点指向NULL } return root; } };
力扣题目链接
这道题目说是二叉树,但116题目说是完整二叉树,其实没有任何差别,一样的代码一样的逻辑一样的味道
C++代码:
class Solution { public: Node* connect(Node* root) { queueque; if (root != NULL) que.push(root); while (!que.empty()) { int size = que.size(); vector vec; Node* nodePre; Node* node; for (int i = 0; i < size; i++) { if (i == 0) { nodePre = que.front(); // 取出一层的头结点 que.pop(); node = nodePre; } else { node = que.front(); que.pop(); nodePre->next = node; // 本层前一个节点next指向本节点 nodePre = nodePre->next; } if (node->left) que.push(node->left); if (node->right) que.push(node->right); } nodePre->next = NULL; // 本层最后一个节点指向NULL } return root; } };
力扣题目链接
给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
说明: 叶子节点是指没有子节点的节点。
示例:
给定二叉树 [3,9,20,null,null,15,7],
返回它的最大深度 3 。
使用迭代法的话,使用层序遍历是最为合适的,因为最大的深度就是二叉树的层数,和层序遍历的方式极其吻合。
在二叉树中,一层一层的来遍历二叉树,记录一下遍历的层数就是二叉树的深度,如图所示:
所以这道题的迭代法就是一道模板题,可以使用二叉树层序遍历的模板来解决的。
C++代码如下:
class Solution { public: int maxDepth(TreeNode* root) { if (root == NULL) return 0; int depth = 0; queueque; 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; } };
力扣题目链接
相对于 104.二叉树的最大深度 ,本题还也可以使用层序遍历的方式来解决,思路是一样的。
需要注意的是,只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点
代码如下:(详细注释)
class Solution { public: int minDepth(TreeNode* root) { if (root == NULL) return 0; int depth = 0; queueque; 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: TreeNode* invertTree(TreeNode* root) { queueque; if (root != NULL) que.push(root); while (!que.empty()) { int size = que.size(); for (int i = 0; i < size; i++) { TreeNode* node = que.front(); que.pop(); swap(node->left, node->right); // 节点处理 if (node->left) que.push(node->left); if (node->right) que.push(node->right); } } return root; } };
我们来看一下递归三部曲:
确定递归函数的参数和返回值
参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。
返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*
。
TreeNode* invertTree(TreeNode* root)
确定终止条件
当前节点为空的时候,就返回
if (root == NULL) return root;
确定单层递归的逻辑
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。
swap(root->left, root->right); invertTree(root->left); invertTree(root->right);
基于这递归三步法,代码基本写完,C++代码如下:
class Solution { public: TreeNode* invertTree(TreeNode* root) { if (root == NULL) return root; swap(root->left, root->right); // 中 invertTree(root->left); // 左 invertTree(root->right); // 右 return root; } };
跳过
力扣题目链接
给定一个二叉树,检查它是否是镜像对称的。
最后递归的C++整体代码如下:
class Solution { public: bool compare(TreeNode* left, TreeNode* right) { // 首先排除空节点的情况 if (left == NULL && right != NULL) return false; else if (left != NULL && right == NULL) return false; else if (left == NULL && right == NULL) 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 isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理) return isSame; } bool isSymmetric(TreeNode* root) { if (root == NULL) return true; return compare(root->left, root->right); } };
跳过