[daily force deduction 29] symmetric binary tree

Posted by K3nnnn on Mon, 24 Jan 2022 17:38:31 +0100

1, Title [LeetCode-101]

Give you the root node of a binary tree, root, and check whether it is axisymmetric.

Example 1:

Input: root = [1,2,2,3,4,4,3]

Output: true

Example 2:

 

Input: root = [1,2,2,null,3,null,3]

Output: false

Tips:

  • The number of nodes in the tree is in the range [1, 1000]
  • -100 <= Node.val <= 100

Advanced: can you use recursive and iterative methods to solve this problem?

2, Train of thought

Observe the meaning of the question. A tree is a symmetrical tree if and only if the left and right son values of the root node are equal (if any) and the left and right subtrees are symmetrical to each other. The recursive embodiment of the symmetry between the left and right subtrees is that if any node of the left subtree is recursively traversed to the left son, the result should be consistent with that of the right son of the node at the corresponding position of the right subtree.

Therefore, first take the left and right child nodes of the root node, and first judge whether the values of the left and right children of the root node are equal. If not, return false. Then you can write recursive sub methods to judge whether the two subtrees are symmetrical.

By the symmetry of two trees, overloaded isSymmetric is used as a recursive sub algorithm, and the parameters are two number nodes. Similarly, judge whether two nodes are empty. After judging the recursive basis (two nodes are empty at the same time, return true; one of the two nodes is empty, return false), start to judge whether both nodes are not empty. Here, it recursively judges whether the pair of t1 left son and t2 right son is satisfied and whether the pair of t1 right son and t2 left son is satisfied. When the subtrees are symmetrical, it finally judges whether the value of t1 is the same as that of t2, and returns true. Here, you recurse down first, and then judge the "current recursion" node. Therefore, it is a post order traversal.

/**
 * 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 isSymmetric(TreeNode* root) {
        if(root->left == nullptr && root->right == nullptr)//There is no degradation of child nodes in the root node
            return true;
        if((root->left != nullptr && root->right == nullptr) || (root->right != nullptr && root->left == nullptr) )//There is only one degradation of the left and right child nodes of the root node
            return false;
        else//When the root node has two child nodes
            return isSymmetric(root->left, root->right);
    }
    bool isSymmetric(TreeNode* t1, TreeNode* t2){
        if(t1 == nullptr && t2 == nullptr)//Degradation of two empty nodes
            return true;
        if((t1 == nullptr && t2 != nullptr) || (t2 == nullptr && t1 != nullptr))//Degradation of two nodes with only one empty node
            return false;
        else//When both nodes are not empty
        {
            if(isSymmetric(t1->left, t2->right)&&isSymmetric(t1->right, t2->left))//First, recursively judge whether the nodes of the subtree meet the symmetry.
                return t1->val == t2->val;
            else return false;
        }
    }
};

 

3, Official solution (source: LeetCode)

Method 1: recursion

If the left subtree and the right subtree of a tree are mirror symmetrical, then the tree is symmetrical.

 

Therefore, the problem can be transformed into: under what circumstances are two trees mirror each other?

If the following conditions are met at the same time, the two trees mirror each other:

  • Their two root nodes have the same value
  • The right subtree of each tree is mirrored and symmetrical with the left subtree of another tree

We can implement such a recursive function to traverse the tree by "synchronously moving" two pointers. At first, the p pointer and q pointer point to the root of the tree, and then when p moves right, q moves left, and when p moves left, q moves right. Check whether the values of the current p and q nodes are equal each time. If they are equal, judge whether the left and right subtrees are symmetrical.

class Solution {
public:
    bool check(TreeNode *p, TreeNode *q) {
        if (!p && !q) return true;
        if (!p || !q) return false;
        return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
    }

    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }
};

Author: LeetCode-Solution
 Link: https://leetcode-cn.com/problems/symmetric-tree/solution/dui-cheng-er-cha-shu-by-leetcode-solution/
Source: force buckle( LeetCode)
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Complexity analysis

Suppose there are n nodes in the tree.

  • Time complexity: this tree is traversed here, and the progressive time complexity is O(n).
  • Space complexity: the space complexity here is related to the stack space used by recursion. The number of recursion layers here does not exceed n, so the progressive space complexity is O(n).

Method 2: iteration

In method 1, we use the recursive method to judge the symmetry, so how to use the iterative method? First, we introduce a queue, which is a common way to rewrite a recursive program into an iterative program. During initialization, we queue the root node twice. Extract two nodes each time and compare their values (every two consecutive nodes in the queue should be equal, and their subtrees mirror each other), and then insert the left and right child nodes of the two nodes into the queue in the opposite order. When the queue is empty, or we detect tree asymmetry (that is, two unequal continuous nodes are taken from the queue), the algorithm ends.

class Solution {
public:
    bool check(TreeNode *u, TreeNode *v) {
        queue <TreeNode*> q;
        q.push(u); q.push(v);
        while (!q.empty()) {
            u = q.front(); q.pop();
            v = q.front(); q.pop();
            if (!u && !v) continue;
            if ((!u || !v) || (u->val != v->val)) return false;

            q.push(u->left); 
            q.push(v->right);

            q.push(u->right); 
            q.push(v->left);
        }
        return true;
    }

    bool isSymmetric(TreeNode* root) {
        return check(root, root);
    }
};

Author: LeetCode-Solution
 Link: https://leetcode-cn.com/problems/symmetric-tree/solution/dui-cheng-er-cha-shu-by-leetcode-solution/
Source: force buckle( LeetCode)
The copyright belongs to the author. For commercial reprint, please contact the author for authorization. For non-commercial reprint, please indicate the source.

Complexity analysis

  • Time complexity: O(n), the same as "method 1".
  • Spatial complexity: a queue needs to be used to maintain nodes. Each node can enter the queue and leave the queue at most once. There will be no more than n points in the queue, so the progressive spatial complexity is O(n).

4, Learning experience

Optimization of recursive code

① The effective use of the use order of & & and 𞓜 under the two conditional statements can simplify the code

For the judgment of degradation, the official solution only uses two short if statements.

if (!p && !q) return true;

if (!p || !q) return false;

Because the first condition is! q and! p is satisfied at the same time, and then return the result directly. So when you go to the second conditional statement, it will not! p and! q is satisfied at the same time. If the second conditional statement is satisfied, it must be! p and! q both have and only one satisfaction. Therefore, you can safely return false. It's very concise.

According to the Enlightenment of official interpretation, the author's code can also be simplified

if(root->left == nullptr && root->right == nullptr)           	return true;
if((root->left != nullptr && root->right == nullptr) || (root->right != nullptr && root->left == nullptr) )
return false;

The second line can be directly changed to:

if(root->left == nullptr || root->right == nullptr )
return false;

It has been aesthetically improved.

At the same time, root - > left = = nullptr can also write directly! root->left

② The judgment of the left and right child nodes of the root node in the author's code is the same as that of other nodes, and can also be incorporated into the category of sub algorithm. It is not necessary to spend so many lines in the main algorithm to judge the left and right child nodes of the root node.

Therefore, the main method can write one line directly:

bool isSymmetric(TreeNode* root) {
            return isSymmetric(root->left, root->right);
    }

Topics: C++ Algorithm leetcode