目录
LeetCode226.反转二叉树
给你一棵二叉树的根节点
root
,翻转这棵二叉树,并返回其根节点。
思路:将二叉树反转,想要达到此效果,那么只需将每一个结点的左右结点都交换一次即可。
适合的遍历方法有前序遍历和后序遍历、以及层序遍历,中序遍历不太适合,因为可能有的结点会被交换两次,最后得出的结果有误。
前序遍历和后序遍历的代码相差不大,只是有的代码顺序可能需要交换,这里以前序遍历来说明。
1. 前序遍历
1.1 递归法
//前序遍历
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return NULL;
swap(root -> left, root -> right);//中
invertTree(root -> left);//左
invertTree(root -> right);//右
return root;
}
1.2 迭代法
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if(root != NULL) 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;
}
1.3 统一迭代法
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if(root != NULL) st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
if(node != NULL){
st.pop();
if(node -> right) st.push(node -> right);//右
if(node -> left) st.push(node -> left);//左
st.push(node);
st.push(NULL);//中
}else{
st.pop();
node = st.top();
st.pop();
swap(node -> left, node -> right);
}
}
return root;
}
2. 层序遍历
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;
}
当然,如果是需要使用中序的方法,也不是不行,只要将可能交换两次的操作代码替换掉即可。下面将展示使用中序的方法,
3.(类)中序遍历法
3.1 递归法
使用递归法来进行交换,将中序遍历中本来要递归的右子树的语句替换成左子树,这样可以避免中间结点交换两次,造成错误的结果。
当然,此时也不叫中序遍历了,它只是有中序遍历的递归壳子,对其进行了改进,使得其能够适应本题。
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return NULL;
invertTree(root -> left);
swap(root -> left, root -> right);
invertTree(root -> left); //这里依然是交换左子数
return root;
}
3.2 统一迭代法
当然,使用统一迭代的方法是可以使用中序遍历的方法的,原因在于递归中使用的是指针,而这里使用的是栈,避免了多次交换的错误。
TreeNode* invertTree(TreeNode* root) {
stack<TreeNode*> st;
if(root != NULL) st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
if(node != NULL){
st.pop();
if(node -> right) st.push(node -> right);//右
st.push(node);
st.push(NULL);//中
if(node -> left) st.push(node -> left);//左
}else{
st.pop();
node = st.top();
st.pop();
swap(node -> left, node -> right);
}
}
return root;
}
LeetCode101.对称二叉树
给你一个二叉树的根节点
root
, 检查它是否轴对称。
思路:比较左右两棵子树的对应位置元素是否完全一致
这里有递归法和迭代法两种。
1.递归法
这里需要注意递归终止条件的判断,当一方为空一方不为空,或者两方都不为空但是值不相等时,直接返回false;当两方都为空时,返回true;当两方都不为空并且值相等时,继续进行其他位置判断。
bool compare(TreeNode* left, TreeNode* right){
if(left == NULL && right != NULL) return false;
else if(left != NULL && right == NULL) return false;//左右结点一方为空则返回false
else if(left == NULL && right == NULL) return true;
else if(left -> val != right -> val) return false;
//左右结点都不为空,但是值不等,返回false
//下面是剩下了左右结点不为空且值相等的情况
bool outside = compare(left -> left, right -> right);
bool inside = compare(left -> right, right -> left);
bool result = outside && inside;
return result;
}
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
return compare(root -> left, root -> right);
}
这是对上面代码的精化,如果不习惯或者不熟悉,建议不要采用这种方式。
bool compare(TreeNode* left, TreeNode* right){
if(left == NULL && right != NULL) return false;
else if(left != NULL && right == NULL) return false;//左右结点一方为空则返回false
else if(left == NULL && right == NULL) return true;
else if(left -> val != right -> val) return false;
//左右结点都不为空,但是值不等,返回false
else return compare(left -> left, right -> right) && compare(right -> left, left -> right);
//下面是剩下了左右结点不为空且值相等的情况
}
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
return compare(root -> left, root -> right);
}
2.迭代法
这里选用了队列或者栈来处理,条件判断逻辑和递归的时候是一样的,这里需要注意的是放入元素时的顺序一定要相对应,这样才能正确比较。
2.1 使用队列解决
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
queue<TreeNode*> que;
que.push(root -> left);
que.push(root -> right);
while(!que.empty()){
TreeNode* left = que.front(); que.pop();
TreeNode* right = que.front(); que.pop();
if(left == NULL && right == NULL) continue;
//此时比较到了叶子结点,都为空,说明相等,继续循环
if(left == NULL || right == NULL || left -> val != right -> val) return false;
//当两个结点一方为空,一方不为空,或者两者都不为空,但是值不一样时,返回false
que.push(left -> left);
que.push(right -> right);
que.push(left -> right);
que.push(right -> left);
//继续放入结点,按照对称位置放置,方便取出进行比较
}
return true;
}
2.2 使用栈解决
bool isSymmetric(TreeNode* root) {
if(root == NULL) return true;
stack<TreeNode*> st;
st.push(root -> left);
st.push(root -> right);
while(!st.empty()){
TreeNode* left = st.top(); st.pop();
TreeNode* right = st.top(); st.pop();
if(left == NULL && right == NULL) continue;
//此时比较到了叶子结点,都为空,说明相等,继续循环
if(left == NULL || right == NULL || left -> val != right -> val) return false;
//当两个结点一方为空,一方不为空,或者两者都不为空,但是值不一样时,返回false
st.push(left -> left);
st.push(right -> right);
st.push(left -> right);
st.push(right -> left);
//继续放入结点,按照对称位置放置,方便取出进行比较
}
return true;
}
LeetCode104.二叉树的最大深度
给定一个二叉树
root
,返回其最大深度。二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。
思路:对于二叉树求深度或者求高度的问题,都有相似之处。深度是从根节点到某一结点之间经过的结点数或者链路数;高度则是从根节点到叶子结点所经过的结点树或者链路数。
这里采用递归法以及迭代法来解决问题。
1.递归法
递归法采用了后序遍历的写法,首先对左子树的深度进行计算,再计算右子树的深度,最后返回的时候取两者的最大值。
int getDepth(TreeNode* cur){
if(cur == NULL) return 0;
int leftmax = getDepth(cur -> left) + 1;//统计左子树的最大深度
int rightmax = getDepth(cur -> right) + 1;//统计右子树的最大深度
return max(leftmax, rightmax);//取最大值
}
int maxDepth(TreeNode* root) {
return getDepth(root);
}
当然还可以采用前序遍历的写法,这也是深度计算的真正逻辑所在。
首先使result与depth进行比较,取其大值,然后分别对左、右子树遍历,这里面涉及到了回溯,使得深度增减。
int result = 0; //统计最大深度
void getDepth(TreeNode* cur, int depth){
result = result > depth ? result : depth;//每一次递归获取最大值
if(cur -> left == NULL && cur -> right == NULL) return;//到达叶子结点
if(cur -> left){
depth ++;
getDepth(cur -> left, depth);
depth --; //回溯需要减1
}
if(cur -> right){
depth ++;
getDepth(cur -> right, depth);
depth --;//回溯需要减1
}
return;
}
int maxDepth(TreeNode* root) {
if(root == NULL) return result;
int depth = 1;
getDepth(root, depth);
return result;
}
这是对上面代码的一种精化,前期如果不熟悉,建议不要使用,不然不方便理解。
int result = 0; //统计最大深度
void getDepth(TreeNode* cur, int depth){
result = result > depth ? result : depth;//每一次递归获取最大值
if(cur -> left == NULL && cur -> right == NULL) return;//到达叶子结点
if(cur -> left) getDepth(cur -> left, depth + 1);
if(cur -> right) getDepth(cur -> right, depth + 1);//简化判断逻辑
}
int maxDepth(TreeNode* root) {
if(root == NULL) return result;
int depth = 1;
getDepth(root, depth);
return result;
}
2.迭代法
这里的迭代法采用了层序遍历的方法。本身层序遍历就是能够计算出层的大小,根节点到最深叶子结点的深度即为高度,这样在层序遍历过程中depth逐渐增加,等到最后循环结束时,即可得到最后的最大深度。
int maxDepth(TreeNode* root) {
queue<TreeNode*> que;
if(root != NULL) que.push(root);
int depth = 0;
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;
}
LeetCode111.二叉树的最小深度
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
思路:求最小深度和最大深度虽然看起来很像,但实际上存在较大差别。主要差别就是对找到最小值时的结点判断,当结点左节点为空、右节点不为空或者左节点不为空、右节点为空时,此时该结点不是我们要找的最小深度结点,因为它不是叶子结点。
这里同样采用递归法和迭代法来进行讲解。
1. 递归法
递归法采用了后序遍历的顺序来处理,首先是获取左边子树的深度,然后是右边子树的深度,当存在左子树为空,右子树不为空时,返回1+右子树的长度;而当右子树为空,左子树不为空时,则返回1+左子树的长度,当两者都不为空时,则选择其中较小者+1返回。
int getDepth(TreeNode* cur){
if(cur == NULL) return 0;
int leftdepth = getDepth(cur -> left); //左
int rightdepth = getDepth(cur -> right); //右
//中
if(cur -> left == NULL && cur -> right != NULL){
//当某个结点的左节点为空时,说明最小深度出现在右子树
return 1 + rightdepth;
}
if(cur -> left != NULL && cur -> right == NULL){
//当某个结点的右节点为空时,说明最小深度出现在左子树
return 1 + leftdepth;
}
int result = 1 + min(leftdepth, rightdepth);
//当某个结点左右子树不为空时,取最短深度,返回result
return result;
}
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
return getDepth(root);
}
这是对上面代码的一个精化。
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
if(root -> left == NULL && root -> right != NULL){
return 1 + minDepth(root -> right);
}
if(root -> left != NULL && root -> right == NULL){
return 1 + minDepth(root -> left);
}
return 1 + min(minDepth(root -> right), minDepth(root -> left));
}
当然,除了使用后序的方法,还可以使用前序的顺序处理。首先每次进入递归对result的值判断,与depth比较,取最小值;然后左边子树不为空,则尝试计算左边子树的深度;右边子树不为空,尝试计算右边子树的深度。最后返回的是经过左右两棵子树经过比较过后的最小深度值,返回即可。
int result = INT_MAX;
void getDepth(TreeNode* cur, int depth){
if(cur == NULL) return;
if(cur -> left == NULL && cur -> right == NULL){
result = min(result, depth); //中
}
if(cur -> left) getDepth(cur -> left, depth + 1);//左
if(cur -> right) getDepth(cur -> right, depth + 1);//右
}
int minDepth(TreeNode* root) {
if(root == NULL) return 0;
int depth = 1;
getDepth(root, depth);
return result;
}
2.遍历法
遍历法同样采用了层序遍历的思路来进行,在进入每层时depth增加,当某个结点左子树为空且右子树为空时,立即返回depth,这就是要找的最小深度值。
int minDepth(TreeNode* root) {
queue<TreeNode*> que;
if(root != NULL) que.push(root);
int depth = 0;
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 == NULL && node -> right == NULL){
//当出现一个结点的左右结点皆为空时,到达了最小深度处
return depth;
}
}
}
return depth;
}
感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。
如果有什么问题欢迎评论区讨论!