"Leetcode" 101. Symmetric Binary Tree

101. Symmetric Binary Tree

Given a binary tree, check whether it is mirror-symmetrical.

 

 

Ideas

"First think about it clearly, to determine which two nodes are to be compared in a symmetric binary tree, and not the left and right nodes to be compared!"

Regarding whether the binary tree is symmetrical, it is necessary to compare whether the left subtree and the right subtree of the root node are flipped with each other. Understand this point. "In fact, we want to compare two trees (the two trees are the root nodes). Left and right subtrees)” , so in the process of recursive traversal, two trees must be traversed at the same time.

So what if it compares?

The comparison is whether the inner and outer elements of the two subtrees are equal. as the picture shows:

 

 

So what should the order of traversal be?

The traversal of this question can only be "post-order traversal", because we need to judge whether the inner node and outer node of the two subtrees are equal through the return value of the recursive function.

"It is precisely because it is necessary to traverse two trees and compare the inner and outer nodes, so to be precise, the traversal order of a tree is left and right center, and the traversal order of a tree is right left center."

But it can be understood as a post-order traversal, although it is not strictly a post-order traversal on a tree.

In fact, the post-order can also be understood as a kind of backtracking. Of course, this is a digression, and I will focus on it when I talk about backtracking.

When it comes to this, everyone may feel that I am a bit long-winded, how can there be so much truth, just come up and do it. Don't worry, what I said is in the code explanation below.

So let's first take a look at how to write the recursive method code.

Recursion

Recursive trilogy

  1. Determine the parameters and return value of the recursive function

Because what we want to compare is whether the two subtrees of the root node are flipped to each other, and then determine whether the tree is a symmetric tree, so the two trees to be compared are naturally the left subtree node and the right subtree node.

The return value is naturally bool type.

code show as below:

bool compare(TreeNode* left, TreeNode* right)
  1. Determine termination conditions

To compare the values ​​of two nodes that are not the same, we must first make it clear that the two nodes are empty! Otherwise, the null pointer will be operated when comparing values ​​later.

The cases where the node is empty include: ( "Note that we are not actually comparing the left child and the right child, so I call it the left node and the right node as follows" )

  • The left node is empty, the right node is not empty, asymmetrical, return false
  • Left is not empty, right is empty, asymmetric return false
  • Left and right are empty, symmetrical, return true

At this time, the situation that the node is empty has been eliminated, so the left and right nodes are not empty:

  • Both left and right are not empty, compare node values, return false if they are not the same

At this time, the left and right nodes are not empty, and the values ​​are not the same, we also deal with the situation.

code show as below:

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

Note that in the last case above, I did not use else, but elseif, because after we eliminated all of the above, what remains is the case where the left and right nodes are not empty and the values ​​are the same.

  1. Determine the logic of single recursion

At this time, the logic of single-layer recursion is entered. The logic of single-layer recursion is to deal with the situation where the right nodes are not empty and the values ​​are the same.

  • Compare whether the outer side of the binary tree is symmetrical: what is passed in is the left child of the left node, and the right child of the right node.
  • Compare whether the internal test is symmetrical, pass in the right child of the left node and the left child of the right node.
  • If the left and right sides are symmetrical, it returns true, and if one side is asymmetrical, it returns false.

code show as below:

bool outside = compare(left->left, right->right);   // 左子树:左、 右子树:右
bool inside = compare(left->right, right->left);    // 左子树:右、 右子树:左
bool isSame = outside && inside;                    // 左子树:中、 右子树:中(逻辑处理)
return isSame;

In the above code, we can see the traversal method used, the left subtree is left and right, and the right subtree is right and left, so I also call this traversal sequence "post-order traversal" (though not strictly post-order traversal).

The final recursive C++ overall code is as follows:

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);
    }
};

"The code I gave is not concise, but the logic of each step of the judgment is clearly depicted."

If you look at the various concise codes on the Internet, it looks really simple, but a lot of logic is covered up, and the problem solution may not clearly explain the covered up logic.

"Blindly copied it, and the result is: I found that this is a "simple question" and passed it silly, but the real logic of every step of the judgment may not be clear."

Of course, I can organize the above code as follows:

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;
        else return compare(left->left, right->right) && compare(left->right, right->left);

    }
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        return compare(root->left, root->right);
    }
};

"This code is very concise, but it hides a lot of logic, is unclear, and the recursive trilogy is completely invisible here."

"So I suggest that when you do the questions, you must think clearly about the logic and what to do at each step. After you think about all the conditions of the question and write the corresponding code, you can pursue the effect of concise code."

Iterative method

We can also use the iterative method for this topic, but it should be noted that the iterative method here is not an iterative writing method in front, middle and post order, because the essence of this question is to judge whether the two trees are flipped, in fact, it is not the so-called binary tree traversal The relationship between the first, the middle and the last.

Here we can use the queue to compare whether the two trees (left and right subtrees of the root node) are flipped with each other, ( "Note that this is not a sequence traversal" )

Use queue

Use the queue to determine whether the inner and outer sides of the left and right subtrees of the root node are equal, as shown in the animation:

 

 

The following conditional judgment and recursive logic are the same.

code show as below:

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;
    }
};

Use stack

If you are careful, you can actually find that this iteration method actually puts the elements to be compared in the left and right subtrees into a container in order, and then takes them out in pairs for comparison. In fact, it is also possible to use the stack.

Just change the queue to the stack intact, and I also give the code below.

class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (root == NULL) return true;
        stack<TreeNode*> st; // 这里改成了栈
        st.push(root->left);
        st.push(root->right);
        while (!st.empty()) {
            TreeNode* leftNode = st.top(); st.pop();
            TreeNode* rightNode = st.top(); st.pop();
            if (!leftNode && !rightNode) {
                continue;
            }
            if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) {
                return false;
            }
            st.push(leftNode->left);
            st.push(rightNode->right);
            st.push(leftNode->right);
            st.push(rightNode->left);
        }
        return true;
    }
};

to sum up

This time we have deeply analyzed the "simple problem" of a binary tree. You will find that it is not easy to really figure out the problem. There is still a gap between accepting on the leetcode and mastering it.

We introduced the recursive method and iterative method. Recursion still solves this problem through the recursive trilogy. If you only look at the condensed code, you can't see how the recursive trilogy solves the problem.

In the iterative method, we use queues. It should be noted that this is not a sequence traversal, and only a container is used to store the elements we want to compare in pairs. After knowing this essence, we find that: use queues, use stacks, and even You can use arrays.

If you have already done this topic, you can look at this topic again after reading the article, think about it, and you will find something different!

This article: https://github.com/youngyangyang04/leetcode-master already included, there is also the question Raiders leetcode brush, brush each type classic topic title sequence, mind map, you can fork into their own warehouses , empty look You will definitely gain something, if it helps you, give a star to support it!

My B station (there are algorithm videos and programming related knowledge I explained) : https://space.bilibili.com/525438321

I am Carl, a programmer, and a senior brother of Harbin Engineering. I have been engaged in technology research and development for Tencent and Baidu for many years. I used my spare time to brush leetcode. More exciting algorithm articles are available:  Code Random Thoughts.    After paying attention, reply "Java" "C++" "python" "Resume template" and so on. With the learning materials I have compiled for many years, you can add me to   WeChat , remark "personal profile" + "group review questions", and pull you into the review group (no ads, pure personal sharing), I analyze a classic topic every day. Every topic I choose is not isolated, but from the shallower to the deeper. If you follow the rhythm and read each article continuously, you will definitely be integrated.

Guess you like

Origin blog.csdn.net/youngyangyang04/article/details/108889652