【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析

前言

前几天朋友在群里发了张图:简单来说,负负得正

【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析_第1张图片
当时感觉挺好笑的,结果今天就被我碰上了。

LeetCode.NO.226.翻转二叉树

Given the root of a binary tree, invert the tree, and return its root.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/invert-binary-tree
【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析_第2张图片
想好一会儿,给了一种很烦的递归思路:

void Swap(struct TreeNode* root)
{
     
    struct TreeNode* tmp = root->left;
    root->left = root->right;
    root->right = tmp;
}
void _invertTree(struct TreeNode* root, struct TreeNode* left, struct TreeNode* right)
{
     
    if(left == NULL && right == NULL)
    {
     
        return;
    }
    if(left != NULL && right != NULL)
    {
     
        _invertTree(left, left->left, left->right);
        _invertTree(right, right->left, right-> right);
        Swap(root);
    }
    if(left == NULL && right != NULL)
    {
     
        _invertTree(right, right->left, right-> right);
        Swap(root);
    }
    //上一句结束后,进行了一次Swap,left不是变成right,right不是变成left了?那下面的if应该执行下去!?
    if(left != NULL && right == NULL)
    {
     
        _invertTree(left, left->left, left->right);
        Swap(root);
    }
}

struct TreeNode* invertTree(struct TreeNode* root)
{
     
    if(root == NULL)
        return root;
    _invertTree(root, root->left, root->right);
    return root;
}

提交之后,过了

但是又看了看,看出了问题:

	if(left == NULL && right != NULL)
    {
     
        _invertTree(right, right->left, right-> right);
        Swap(root);
    }
    //上一句结束后,left不是变成right,right不是变成left了?那下面的if应该执行下去!?
    if(left != NULL && right == NULL)
    {
     
        _invertTree(left, left->left, left->right);
        Swap(root);
    }

第一次Swap,left不是变成right,right不是变成left了?那下面的if还应该执行下去!?可是我们应该让这次递归结束,不能再去交换了啊!!但是他错误地交换了之后,为什么还能AC???

理应这么写,每次Swap后,return跳出当前递归

	if(left == NULL && right != NULL)
    {
     
        _invertTree(right, right->left, right-> right);
        Swap(root);
        return;
    }
    if(left != NULL && right == NULL)
    {
     
        _invertTree(left, left->left, left->right);
        Swap(root);
    }

我怎么也看不出来,为什么没return还能过。

研究过程

我们打开VS,只能自己写好测试用例,调试!!!

测试用例长这样:

【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析_第3张图片

#define _CRT_SECURE_NO_WARNINGS 1
#include 
#include 
typedef struct BTNode
{
     
	struct BTNode* right;
	struct BTNode* left;
	int val;
}BTNode;

BTNode* BTNodeCreate(int x)
{
     
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->val = x;
	root->left = NULL;
	root->right = NULL;
	return root;
}


void Swap(struct BTNode* root)
{
     
    struct BTNode* tmp = root->left;
    root->left = root->right;
    root->right = tmp;
}
void _invertTree(struct BTNode* root, struct BTNode* left, struct BTNode* right)
{
     
    if (left == NULL && right == NULL)
    {
     
        return;
    }
    if (left != NULL && right != NULL)
    {
     
        _invertTree(left, left->left, left->right);
        _invertTree(right, right->left, right->right);
        Swap(root);
    }
    if (left == NULL && right != NULL)
    {
     
        _invertTree(right, right->left, right->right);
        Swap(root);
    }
    if (left != NULL && right == NULL)
    {
     
        _invertTree(left, left->left, left->right);
        Swap(root);
    }
}

struct BTNode* invertTree(struct BTNode* root)
{
     
    if (root == NULL)
        return root;
    _invertTree(root, root->left, root->right);
    return root;
}
int main()
{
     
	BTNode* node1 = BTNodeCreate(1);
	BTNode* node2 = BTNodeCreate(2);
	BTNode* node3 = BTNodeCreate(3);
	node1->right = node2;
	node2->right = node3;

    invertTree(node1);
   

   
	return 0;
}

再把这段有异议的代码拿出来:

if (left == NULL && right != NULL)
    {
     
        _invertTree(right, right->left, right->right);
        Swap(root);
    }
    /*位置1*/
    if (left != NULL && right == NULL)
    {
     
        _invertTree(left, left->left, left->right);
        Swap(root);
    }

我们在位置1处停下。

那么按照我的理解,此时:

left == val为3的节点
right == NULL
root->left == val为3的节点
root->right == NULL

监视:
【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析_第4张图片
root->left 和 root->right 确实交换了,但left和right没有交换!!!

原来问题出在这里:

left 和 right 是一份root->left 和 root->right的临时拷贝,root->left和root-right确实交换了,但身为临时拷贝的left和right还是没交换,该是谁就还是谁!所以下面 if 语句根本就不会进去!

本题优化解答

这个递归写的有点长,因为又用了一个子函数_invertTree,我们试着不用子函数来做一下,让代码少一点

struct TreeNode* invertTree(struct TreeNode* root){
     
    if(!root)
        return NULL;
    struct TreeNode* left = root->left;
    struct TreeNode* right = root->right;
    root->right = invertTree(left);
    root->left = invertTree(right);
    return root;
}

简单说明以下,递归,简单来说就是两个字——分治
本题想要翻转一棵二叉树,那么,我们可以先翻转左子树和右子树;
对于左子树,我们可以先翻转左子树的左子树和左子树的右子树
对于右子树,我们可以先翻转右子树的左子树和右子树的右子树
… … … …

以下是递归展开图,测试用例依然是上面那个[1,null,2,null,3]
【LeetCode】NO.226.翻转二叉树的一次负负得正还能AC的调试画图分析_第5张图片

总结

好了,总结一波

  1. 发现可疑问题并且眼睛看看不出来的时候,调试!
  2. 脑子不够用,比如递归的时候脑子一层一层展开真的不够用时候,画图!画递归展开图!

你可能感兴趣的:(leetcode刷题,leetcode,算法,二叉树,递归算法)