目录
Leetcode 102.二叉树的层序遍历
Leetcode 226.翻转二叉树
Leetcode 101.对称二叉树
Leetcode 102.二叉树的层序遍历
题目链接:Leetcode 102.二叉树的层序遍历
题目描述:给你二叉树的根节点root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
思路:因为要一层一层遍历,因此需要利用队列辅助。因为队列先进先出符合一层一层遍历的逻辑。本题仍然有递归法和迭代法,不过虽然有两种方法,底层逻辑是相同的,只不过实现的方式不同。从根节点出发,根据每层节点个数依次将节点按顺序放入队列中,然后进入下一层。
代码如下:(递归法)
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;
order(root, result, depth);
return result;
}
};
(迭代法)
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr)//如果根节点不为空,加入根节点
que.push(root);
vector<vector<int>> result;
while (!que.empty()) {//只要队列不为空
int size = que.size();//保存每一层的大小
vector<int> vec;
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;
}
};
Leetcode 226.翻转二叉树
题目链接:Leetcode 226.翻转二叉树
题目描述:给你一棵二叉树的根节点root,翻转这棵二叉树,并返回其根节点。
思路:由于要翻转二叉树,因此将每个节点的左右孩子翻转就可以了。至于遍历方式,dfs和bfs都可以,遍历只是个工具,具体的行为需要根据题意灵活转换。这道题需要注意的是中序遍历并不适合本题,中序遍历会将某些节点的左右孩子翻转两次。为什么呢?首先要知道中序遍历的搜索方式是左中右,当所有的左孩子反转之后,我们将中间节点进行翻转,此时的左孩子变成了右孩子,右孩子变成了左孩子,而按照中序遍历,接下来该访问右孩子,但是此时的右孩子就是之前翻转过的左孩子,这导致了最初的左孩子翻转了两次,右孩子没被翻转。
文字看起来很绕对吧?我简单画图举个例子:(图片中箭头代表中序遍历的顺序)
根据这个例子我们可以发现,有两个节点没交换过,有两个节点被交换了两次。不过画图之后我们发现:只要以左中左的遍历方式,仍然可以实现我们想要的“中序遍历”的结果。只不过这样严格意义上不算中序遍历了。
代码如下:(递归dfs,先实现交换再递归,对应前/后序遍历)
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;
}
};
(递归bfs)
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr)
return nullptr;
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
//从最深层开始交换,逐渐向上返回
root->left = right;
root->right = left;
return root;
}
};
(迭代dfs)
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr)
return root;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
swap(node->left, node->right);
//这两个顺序可以调换
if (node->left)
st.push(node->left);
if (node->right)
st.push(node->right);
}
return root;
}
};
(迭代bfs)
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* 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;
}
};
通过上面代码我们发现,其实本质上解题的基础代码还是二叉树的遍历搜索,唯一有区别的是本题搜索之后需要交换左右孩子节点。
Leetcode 101.对称二叉树
题目链接:Leetcode 101.对称二叉树
题目描述:给你一个二叉树的根节点root , 检查它是否轴对称。
思路:首先要思考什么叫做二叉树的轴对称?这可不是简单的比较一个节点的左右孩子就可以了,而是要比较一个结点的左子树和右子树的内测和外侧是否相等。
注:上述图片来源于《代码随想录》
上道题我们提到过:遍历只是个工具,具体的行为需要根据题意灵活转换。因此接下来我们需要思考一下这道题该用什么遍历方式。因为要遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。由于中间节点最后遍历,这和后序遍历类似,因此本题用后序遍历来实现。
代码如下:(递归法)
class Solution {
public:
bool compare(TreeNode* l, TreeNode* r) {
//首先排除空节点的情况
if (l == nullptr && r != nullptr)
return false;
else if (l != nullptr && r == nullptr)
return false;
else if (l == nullptr && r == nullptr)
return true;
//再排除数值不相同的情况
else if (l->val != r->val)
return false;
//数值相同,则递归下一层
bool outside = compare(l->left, r->right);
bool inside = compare(l->right, r->left);
bool isSame = outside && inside;
return isSame;
}
bool isSymmetric(TreeNode* root) {
if (root == nullptr)
return true;
return compare(root->left, root->right);
}
};
(迭代法)
仍然是借助队列来辅助。
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == nullptr)
return true;
queue<TreeNode*> que;
//将根节点左右子树的根节点加入队列
que.push(root->left);
que.push(root->right);
while (!que.empty()) {
TreeNode* lnode = que.front();
que.pop();
TreeNode* rnode = que.front();
que.pop();
if (!lnode && !rnode)
continue; //左右节点均为空,说明对称
//有一个节点为空或者两节点数值不同,说明不对称
if (!lnode || !rnode || (lnode->val != rnode->val))
return false;
que.push(lnode->left); // 加入左节点左孩子
que.push(rnode->right); // 加入右节点右孩子
que.push(lnode->right); // 加入左节点右孩子
que.push(rnode->left); // 加入右节点左孩子
}
return true;
}
};
总结:这几天忙着练车和出去玩,进度有点落下了。不过题目难度还可以,二叉树的很多题目都是以遍历为基础,额外的操作简单思考过后也可以轻松解决。还是那句话:遍历只是个工具,具体的行为需要根据题意灵活转换。
最后,如果文章有错误,请在评论区或私信指出,让我们共同进步!