107.二叉树的层序遍历
难度指数:😀😐😕
二叉树的层序遍历,相当于图论中的广度优先搜索(BFS), 而二叉树的递归遍历,相当于图论中的深度优先搜索(DFS)。
图论中的深搜和广搜可以应用在不同的数据结构中,
在二叉树本身的这个结构中,无法做到层序遍历的,需要借助队列, 借助一个队列来保存每一层里面遍历过的元素。
下面看如何通过队列来层序遍历二叉树: (图论中的广度优先搜索也是依赖队列来实现。)
动画:
代码思路:
不能一上来就把root加入到队列,需要先判断root是否为空。如果root不为空,将第一个节点加入到队列。
queue<TreeNode*> que;
if (root != NULL) que.push(root); //根节点不为空,就将第一个节点加入到队列
//遍历的过程
while (!que.empty()) { //终止条件:队列里没有元素 (说明二叉树里没有元素可以添加到队列里,程序终止)
size = que.size(); //size记录的就是当前层的节点个数 (作用是控制队列里面弹出的节点数量,不控制就不知道队列里应该弹出多少节点)
vector<int> vec; //放一层的元素 (如:第二层的元素4、7 放到一维数组里)
while (size--) { //遍历当前层的元素 (当前层只有一个元素)
node = que.front(); //node获取到队列里的第一个元素
que.pop(); //弹出元素
vec.push_back(node->val); //用一个数组把这个结果记录一下 (相当于这个6放到一维数组里)
if (node->left) {
que.push(node->left);
}
if (node->right) {
que.push(node->right);
}
}
result.push_back(vec);
}
return result;
一维数组在 while循环 结束之后还要放到二维数组里。
🐳最终结果要用一个二维数组result来保存,即最终的结果集。
此时,一层一层节点的遍历已经遍历完了,我们通过size来控制本层的元素,
AC代码: (核心代码模式)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) { //若根节点不为空
que.push(root); //push进队列
}
vector<vector<int>> result; //二维数组,存最终的结果集
//遍历的过程
while (!que.empty()) { //终止条件:队列里没有元素 (说明二叉树里没有元素可以添加到队列,程序终止)
int size = que.size();
vector<int> vec;
while(size--) {
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;
}
};
226.翻转二叉树
难度指数:😀🙂
其实就是两两交换节点的左右孩子,
⚠️注意:交换的是指针,而不是数值!
大部分人在写这道题时,有很多点是没想清楚的,
比如:这道题应该用哪种遍历方式,有两大类遍历方式:1️⃣递归; 2️⃣非递归
递归里面还包含前、中、后序遍历,你要用哪种顺序来遍历?
递归法:
想清楚用哪种遍历方式?
用前序、后序遍历是最合适的;用中序遍历代码有点绕。
写递归一定会要用到递归三部曲
动画:
代码思路:
1️⃣确定递归函数的参数和返回值
//题目要求返回翻转之后新的二叉树的根节点
TreeNode* invertTree(root) {
}
2️⃣确定终止条件
//碰到空节点,终止遍历
if (root == NULL) return root;
3️⃣确定单层递归的逻辑
//交换root的左孩子和右孩子
swap(root->left, root->right); //中
invertTree(root->left); //左
invertTree(root->right); //右
⚠️注意:这里root可能会给你带来歧义,这里的root是力扣上给好的定义,
在本题中,root是指遍历的每一个节点,你操作的变量不一定总是二叉树的根节点。 (这个变量最好不要命名为root)
前序、后序遍历写法:
AC代码: (核心代码模式)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) {
return root;
}
swap(root->left, root->right); //中
invertTree(root->left); //左
invertTree(root->right); //右
return root;
}
};
Q:
swap()可不可以放在最下面,即 左右中 (后序遍历)?A:可以的。但是左中右(中序遍历)就不行!
按照上面这个套路写中序遍历是不行的!
会有以下问题:原先二叉树的左子树被处理了两次,变回原样;导致了右子树没有被处理。
中序遍历写法(不推荐):
需要改动,但代码比较绕!
AC代码: (核心代码模式)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) {
return root;
}
invertTree(root->left); //左
swap(root->left, root->right); //中
invertTree(root->left); //右
return root;
}
};
非递归法(迭代)以及层序遍历依然可以把这道题通过。
未完待续……
101.对称二叉树
难度指数:😀😐
我们在判断一棵二叉树是不是对称二叉树的时候,
本质上是判断根节点的左子树和右子树是否可以相互翻转。
比较的是两个子树的里侧和外侧的元素是否相等。如图所示:
很多人都不清楚自己用的什么遍历顺序,一顿操作猛如虎,通过了就不管了。
二叉树的题目,确定遍历顺序很重要!决定了对这道题的理解深度。
递归法:
想清楚用哪种遍历方式?
🦄这道题只能选择后序遍历(左右中),因为只有后序遍历才能把底部孩子的信息返回给上一层。
在中的处理过程中,就将孩子的处理信息向上一层返回了,
代码思路:
传入的左、右节点,如果:
- 左节点为空,右节点不为空,那么两个二叉树外侧或者内测,比较的两个节点不相同,不相同就一定不可以翻转,return false;
- 左节点不为空,右节点为空,也应该return false;
- 左右都为空,可以翻转,因为至少是对称的;
- 左右都不为空,但是值不相等,return false;
- 左右都不为空,且值相等,应该继续往下遍历,看看它的左右孩子是否也是相等的(向下一层遍历的过程,涉及到递归三部曲的3️⃣)。
1️⃣确定递归函数的参数以及返回值
bool compare(TreeNode* left, 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;
}
}
2️⃣确定终止条件
3️⃣确定单层递归的逻辑
AC代码: (核心代码模式)
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
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 false;
return compare(root->left, root->right);
}
};