目录
LeetCode: 226.翻转二叉树
基本思路
C++代码
LeetCode: 101. 对称二叉树
基本思路
C++代码
LeetCode: 104.二叉树的最大深度
基本思路
C++代码
LeetCode: 111.二叉树的最小深度
基本思路
C++代码
LeetCode: 226.翻转二叉树
力扣题目链接
文字讲解:LeetCode: 226.翻转二叉树
视频讲解:听说一位巨佬面Google被拒了,因为没写出翻转二叉树
基本思路
这个题目可以使用递归、迭代以及层序遍历的方法分别解题。
使用递归的方法,遵循递归三部曲:
- 确定递归函数的参数和返回值。
返回值的话其实也不需要,但是题目中给出的要返回root节点的指针,可以直接使用题目定义好的函数,所以就函数的返回类型为TreeNode*
。
TreeNode* invertTree(TreeNode* root)
- 确定终止条件
当前节点为空的时候,就返回。
if (root == NULL) return root;
- 确定单层递归的逻辑
因为是先前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。(如果使用后序遍历,则可以先反转左右子树,然后交换左右孩子节点。思考:如果使用中序遍历会怎么样?)
//以前序遍历为例
swap(root->left, root->right);//中
invertTree(root->left);//左
invertTree(root->right);//右
如果直接将swap函数放在交换左右孩子节点的话为什么不可以呢?不妨自己模拟一下交换的过程,以下面的图为例,如果先处理2为中间节点的子树,然后再交换4为中间节点的左右孩子节点,这时2为中间节点的子树就变成了4为中间节点的右子树,此时在对右子树进行处理,实际上还是在对2为中间节点的子树进行处理,显然这样就不能实现题目所想达到的效果(有点拗口,但是自己手写一下过程就很容易弄明白了)。那我们如果想要实现中序遍历实际上可以对左孩子节点进行两次处理,即:
//中序遍历
invertTree(root->left);//左
swap(root->left, root->right);//中
invertTree(root->left);//左
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;
}
};
//迭代法
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == NULL) return root;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
swap(node->left, node->right);
if(node->right) st.push(node->right); // 右
if(node->left) st.push(node->left); // 左
}
return root;
}
};
//层序遍历
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
queue<TreeNode*> que;
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;
}
};
LeetCode: 101. 对称二叉树
力扣题目链接
文字讲解:LeetCode: 101. 对称二叉树
视频讲解:新学期要从学习二叉树开始!
基本思路
首先想清楚,判断对称二叉树要比较的是哪两个节点,要比较的可不是左右节点,而是要比较两个树(这两个树是根节点的左右子树),比较的是两个子树的里侧和外侧的元素是否相等。
而这个题目我们依然使用递归的方法进行分析。
- 确定递归函数的参数和返回值。
因为我们需要比较的两个子树(的里侧和外侧的元素值是否相等),并返回true和false。
bool compare(TreeNode* left, TreeNode* right)
- 确定终止条件
显然,在判断两个子树是否相等时,存在五种情况:
- 左节点为空,右节点不为空,此时返回false。
- 左节点不为空,右节点为空,此时返回false。
- 左节点为空,右节点也为空,此时返回true。
- 左节点不为空,右节点也不为空,但是左右节点的值不相同,此时返回false。
- 左节点不为空,右节点也不为空,并且左右节点的值相同,则可以根据此确定单层递归逻辑。
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; // 注意这里我没有使用else,因为剩下的情况就是左右值都不为空,并且值相同
- 确定单层递归逻辑
单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。
- 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
- 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
- 如果左右都对称就返回true ,有一侧不对称就返回false 。
bool outside = compare(left->left, right->right); // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left); // 左子树:右、 右子树:左
bool isSame = outside && inside; // 左子树:中、 右子树:中(逻辑处理)
return isSame;
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);
}
};
//迭代法
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == NULL) 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;
}
// 左右一个节点不为空,或者都不为空但数值不相同,返回false
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;
}
};
LeetCode: 104.二叉树的最大深度
力扣题目链接
文字讲解:LeetCode: 104.二叉树的最大深度
视频讲解:二叉树的高度和深度有啥区别?究竟用什么遍历顺序?很多录友搞不懂
基本思路
以递归法为例,本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度,而根节点的高度就是二叉树的最大深度。
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)
这个题我们可以通过后序遍历,求出根节点的高度也就求得了二叉树的最大深度。
依旧是递归法三部曲:
- 确定递归函数的参数和返回值。
参数传入的是树的根节点,返回值为二叉树的最大深度,为int类型。
int getdepth(TreeNode* node)
- 确定终止条件
如果是空节点就返回,说明此时高度为零。
if (node == NULL) return 0;
- 确定单层递归逻辑
先求左子树的深度,再求出右子树的深度,最后取左右子树深度的最大值+1(包含当前中间节点)即为二叉树的深度。
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
C++代码
class Solution {
public:
int getdepth(TreeNode* node) {
if (node == NULL) return 0;
int leftdepth = getdepth(node->left); // 左
int rightdepth = getdepth(node->right); // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;
}
int maxDepth(TreeNode* root) {
return getdepth(root);
}
};
LeetCode: 111.二叉树的最小深度
力扣题目链接
文字讲解:LeetCode: 111.二叉树的最小深度
视频讲解:看起来好像做过,一写就错!
基本思路
直觉上好像和求最大深度差不多,其实还是差不少的。
本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离也同样是最小深度。
本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解,如图:
需要注意的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。什么是叶子节点呢?左右孩子都为空的节点才是叶子节点!
熟悉的递归法三部曲:
确定递归函数的参数和返回值。
参数需要加入的参数为二叉树的根节点,返回值是二叉树的最小深度,因此为int类型。
int getDepth(TreeNode* node)
确定终止条件
终止条件为直到遇到空节点时停止,此时表示高度为0。
if (node == NULL) return 0;
确定单层递归逻辑
int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
求左右子树的深度依旧和前面的方法一样,,但是求最小深度时,我们可以直接将max函数改为min函数吗?如果仔细模拟全过程可以发现其实是不可以的,以下图为例,前面提到左右孩子都为空的节点才是叶子节点,对于根节点1来讲,如果求min(lefDepth,rightDepth),那么会将没有左孩子的分支算为最小深度。此时最小深度为1,而实际上最小深度应该为3。
因此应该做一个if判断,如果左子树为空且右子树不为空时,最小深度为1+rightDepth;同理如果左子树不为空而右子树为空时,最小深度为1+leftDepth;只有当两边的子树都不为空时,最小深度才是1+min(lefDepth,rightDepth)。
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL) {
return 1 + rightDepth;
}
// 当一个右子树为空,左不为空,这时并不是最低点
if (node->left != NULL && node->right == NULL) {
return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;
C++代码
class Solution {
public:
int getDepth(TreeNode* node) {
if (node == NULL) return 0;
int leftDepth = getDepth(node->left); // 左
int rightDepth = getDepth(node->right); // 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (node->left == NULL && node->right != NULL) {
return 1 + rightDepth;
}
// 当一个右子树为空,左不为空,这时并不是最低点
if (node->left != NULL && node->right == NULL) {
return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;
}
int minDepth(TreeNode* root) {
return getDepth(root);
}
};
//迭代法
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
int depth = 0;
queue<TreeNode*> que;
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;
}
};