7, Binary tree: summary of this week

Monday

This week we began to talk about binary trees You should know this about binary trees!   (opens new window) The theoretical basis of binary tree is explained in. Some students will separate the red black tree from the binary balanced search tree. In fact, the red black tree is a binary balanced search tree. The two trees are not independent, so the underlying implementation mechanism of map, multimap, set and multiset in C + + is a binary balanced search tree. To be more specific, it is a red black tree.

For the definition of binary tree node, the C + + code is as follows:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

For this definition   TreeNode(int x) : val(x), left(NULL), right(NULL) {}   Some students don't know what to do. This is a constructor. Let's say that structures in C language are the ancestors of classes in C + +, so c + + structures can also have constructors. The constructor may not be written, but it is troublesome to new a new node.

For example, there is a constructor that defines a node with an initial value of 9:

TreeNode* a = new TreeNode(9);

If there is no constructor, it should be written as follows:

TreeNode* a = new TreeNode();
a->val = 9;
a->left = NULL;
a->right = NULL;

When introducing the first, middle and last order traversal, there are recursion and iteration (non recursion), and there is a powerful traversal method: morris traversal.

morris traversal is a super advanced algorithm of binary tree traversal algorithm. morris traversal can reduce the spatial complexity of non recursive traversal to O(1). If you are interested, you can check and learn. Compared with a small number, you can hardly take the exam in the interview. In fact, I haven't studied it, so I won't introduce it too much.

Tuesday

stay Binary tree: as soon as you enter recursion, it is as deep as the sea. Since then, offer is a passer-by   (opens new window) The three elements of recursion and the recursive writing of the first, middle and last order are discussed in. In the article, I gave the first, middle and last order of three binary trees on leetcode, but I finished reading it Binary tree: as soon as you enter recursion, it is as deep as the sea. Since then, offer is a passer-by   (opens new window) , it can still solve the preorder traversal of n-ary tree, which are respectively on leetcode

  • Preorder traversal of N-ary tree
  • Postorder traversal of N-ary tree

You can do these two questions again.

Wednesday

stay Binary tree: I heard that recursion can do, stack can also do!   (opens new window) In, we began to use stack to realize recursive writing, that is, the so-called iterative method. Careful students find that the writing method of traversing empty nodes before and after the text is different. In fact, it's almost the same whether the empty node is in the stack or not, but it feels that the empty node is not in the stack is really clear, which is in line with the animation demonstration in the article.

Take the previous sequence as an example. Empty nodes are stacked:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                      // in
            st.pop();
            if (node != NULL) result.push_back(node->val);
            else continue;
            st.push(node->right);                           // right
            st.push(node->left);                            // Left
        }
        return result;
    }
};

The code of traversing empty nodes without putting them on the stack in the preamble: (note the difference between the comment part and the above)

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        stack<TreeNode*> st;
        vector<int> result;
        if (root == NULL) return result;
        st.push(root);
        while (!st.empty()) {
            TreeNode* node = st.top();                       // in
            st.pop();
            result.push_back(node->val);
            if (node->right) st.push(node->right);           // Right (empty nodes are not stacked)
            if (node->left) st.push(node->left);             // Left (empty nodes are not stacked)
        }
        return result;
    }
};

In the process of implementing the iterative method, some students asked: who is better or worse between recursion and iteration?

In terms of time complexity, the iterative method is almost the same as the recursive method (regardless of the function call overhead and the stack overhead generated by function calls), but in terms of space complexity, the recursive overhead will be greater, because recursion requires the system stack to store parameter return values, etc.

Recursion is easier for programmers to understand, but it has poor convergence and is easy to stack overflow. Let's say that recursion is convenient for programmers and difficult for machines (all kinds of stored parameters, all kinds of input and output). In the actual project development process, we should try to avoid recursion! Because the project code parameters and call relationships are complex, it is not easy to control the recursion depth, or even stack overflow.

Thursday

stay Binary tree: can't we unify the writing of the first, middle and last order iteration?   (opens new window) In, we use empty nodes as markers and give a unified pre -, middle - and post order iterative method. At this time, there is another iterative writing method of front, middle and rear order. Then some students asked: does the iterative method of front, middle and rear order have to be written uniformly? This is the norm.

In fact, it's not necessary. You can use whichever you feel is better. However, we must master the iterative writing method of the first, middle and last sequence. It is not because the topic of a certain scene must use iteration, but during the on-site interview, when the interviewer sees that you have smoothly written recursion, he will generally further investigate whether you can write the corresponding iteration.

Friday

stay Binary tree: sequence traversal!   (opens new window) In, we introduce another traversal method of binary tree (the application of breadth first search in graph theory) that is sequence traversal. After reading this article, go to leetcode to brush five questions angrily. The sample diagram of Title 107 in the article is misplaced (forgive me for always shaking my hands in a hurry), but it does not affect everyone's understanding.

Only the students found that "515. Find the maximum value in each tree row" on leetcode is also an application of sequence traversal. It can still be solved in minutes, so it is to solve six channels in one go, ha ha.

Sequence traversal is relatively easy. As long as you master the basic writing method (that is, the framework template), the rest is to make logical modifications when traversing each row of the binary tree.

Saturday

stay Binary tree: can you really flip a binary tree?   (opens new window) In, we fully analyzed a wave of such a simple and classic problem as flipping binary tree. I believe even those who have done this topic will still gain something after reading this article!

In this article, I mean that recursive middle order traversal is not feasible, because using recursive middle order traversal, the left and right children of some nodes will flip twice. If you have to write in recursive middle order, you can also use the following code to avoid the situation that the left and right children of the node flip twice:

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        invertTree(root->left);         // Left
        swap(root->left, root->right);  // in
        invertTree(root->left);         // Note that we still have to traverse the left child, because the middle node has been flipped
        return root;
    }
};

Although the code is OK, it is not a real recursive sequence traversal after all. However, it is possible to use the iterative method to unify the middle order.

The code is as follows:

class Solution {
public:
    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);  // right
                st.push(node);                          // in
                st.push(NULL);
                if (node->left) st.push(node->left);    // Left

            } else {
                st.pop();
                node = st.top();
                st.pop();
                swap(node->left, node->right);          // Node processing logic
            }
        }
        return root;
    }
};

Why is this middle order OK? Because it is traversed by the stack rather than by the pointer, which avoids the situation of flipping twice in the recursive method. You can draw a picture and understand it. It's interesting here.

summary

This week we all explained the binary tree, from theoretical basis to traversal mode, from recursion to iteration, from depth traversal to breadth traversal, and finally used a topic of flipping the binary tree to string up the traversal modes we talked about before.

Tags: C Algorithm data structure

Posted on Sun, 19 Sep 2021 12:42:44 -0400 by mechew