199. Right view of binary tree

Keywords: Algorithm leetcode Dynamic Programming

/**
 * 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:
    vector<int> rightSideView(TreeNode* root) {
        //First, the overall idea is to traverse the sequence directly and install it in layers, so that the last element between each cell can be taken directly
        //Another idea is to start from the node. If you can go right, go right. If you can't go right, go left until the left and right are empty
        vector<int> ans;
        if(root == nullptr)return ans;
        while(root->right != nullptr || root->left != nullptr){
            if(root->right != nullptr){
                ans.push_back(root->val);
                root = root->right;
            }
            else{
                ans.push_back(root->val);
                root = root->left;
            }
        }
        ans.push_back(root->val);
        return ans;
    }
};

The idea above is flawed. Because the one on the far right you find, if you have a depth on the left which is bigger than the right-hand side you are looking for, then the final result will be wrong.

Therefore, the second method listed in the notes, that is, the method implemented above, you soon have this topic of thinking at the beginning. You must verify its correctness in your mind and think about special cases. It can what is the correct principle and see if there is any special case from the principle.

So the principle of this problem is to find the rightmost one, but forget that the left may be deeper than the right, resulting in the final error. The remedy is to make up for the excess, but how do you know which is the longest?

So this method is completely ineffective.

/**
 * 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:
    vector<int> rightSideView(TreeNode* root) {
        //First, the overall idea is to traverse the sequence directly and install it in layers, so that the last element between each cell can be taken directly
        //Another idea is to start from the node. If you can go right, go right. If you can't go right, go left until the left and right are empty
        vector<int> ans;
        queue<TreeNode*> q;

        if(root != nullptr){
            q.push(root);
        }
        while(!q.empty()){
            vector<int> v;
            int size = q.size();
            
            for(int i = 0; i < size; i++){
                root = q.front();
                q.pop();
                v.push_back(root->val);
                if(root->left)q.push(root->left);
                if(root->right)q.push(root->right);
            }
            ans.push_back(v.back());
        }
        return ans;
    }
};

The above implementation uses sequence traversal, and then takes only the last element. Of course, there are areas that can be optimized. For example, since you only take the last one, you can save it with a variable. There is no need to do the same as the previous sequence 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:
    vector<int> rightSideView(TreeNode* root) {
        //First, the overall idea is to traverse the sequence directly and install it in layers, so that the last element between each cell can be taken directly
        //Another idea is to start from the node. If you can go right, go right. If you can't go right, go left until the left and right are empty
        vector<int> ans;
        queue<TreeNode*> q;

        if(root != nullptr){
            q.push(root);
        }
        while(!q.empty()){
            //vector<int> v;
            int v = 0;
            int size = q.size();
            
            for(int i = 0; i < size; i++){
                root = q.front();
                q.pop();
                //v.push_back(root->val);
                v = root->val;
                if(root->left)q.push(root->left);
                if(root->right)q.push(root->right);
            }
            ans.push_back(v);
        }
        return ans;
    }
};

The efficiency of such an upgrade has soared to more than 100%. It was more than 20 percent before.

Of course, the above uses a variable to constantly update and maintain the val value. When exiting, val is naturally the val of the last element of the layer. What if someone specifies the penultimate one, how do you fix it? You can judge the whole condition inside the for loop, because you have the total number of times of execution of this loop. When the number is equal to size - 2, it is the penultimate.

/**
 * 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:
    vector<int> rightSideView(TreeNode* root) {
        //First, the overall idea is to traverse the sequence directly and install it in layers, so that the last element between each cell can be taken directly
        //Another idea is to start from the node. If you can go right, go right. If you can't go right, go left until the left and right are empty
        vector<int> ans;
        queue<TreeNode*> q;

        if(root != nullptr){
            q.push(root);
        }
        while(!q.empty()){
            //vector<int> v;
            int v = 0;
            int size = q.size();
            
            for(int i = 0; i < size; i++){
                root = q.front();
                q.pop();
                //v.push_back(root->val);
                //v = root->val;
                if(root->left)q.push(root->left);
                if(root->right)q.push(root->right);
                if(i == size - 1){v = root->val;}
            }
            
            ans.push_back(v);

        }
        return ans;
    }
};

The above one uses conditional judgment to save the answer. This is more flexible. Maybe it could be more concise

        if(i == size - 1){ans.push_back(root->val);}

After the for loop

ans.push_back(v);

This thing can be removed.

In addition, the first method just mentioned is not no good. The above is realized by sequence traversal, that is, breadth first play. As for the just playing method of depth first, you have been looking for the way on the far right. You may lack some elements because the depth is not the deepest. At this time, you think it is very troublesome to make up the rest and give up directly.

Sometimes a good idea is the lack of persistence and courage to dig and think more.

You only need to record the depth here. You always take the rightmost route (planing the last one) every time, so that when you break through the original depth, the elements you visit must be visible in the right view.

In fact, this idea is opposite to the idea of preorder traversal. Each time you visit the leftmost element of each layer, and this is the rightmost element.

  The above one is also something you need to pay attention to when doing questions!!!!

class Solution {
public:
    void cou(vector<int>& ret, TreeNode* root, int len)
    {
        if(root == nullptr)
            return;
        if(ret.size() < len)
        {
            ret.push_back(root->val);
        }
        cou(ret, root->right, len+1);
        cou(ret, root->left, len+1);
    }
    vector<int> rightSideView(TreeNode* root) {
        vector<int> ret;
        cou(ret, root, 1);
        return ret;
    }
};answer DFS

In fact, the logical framework of preorder traversal is used here.

/**
 * 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:
    void dfs(vector<int>& ans, TreeNode* root, int depth){
        if(root == nullptr){return;}
        if((int)ans.size() < depth){//The size here represents the maximum depth
        //The depth here is the depth of the currently accessed element
            ans.push_back(root->val);
        }
        dfs(ans, root->right, depth + 1);
        dfs(ans, root->left, depth + 1);
    }
    vector<int> rightSideView(TreeNode* root) {
        vector<int> ans;
        dfs(ans, root, 1);
        return ans;
    }
};

There is also a core point to be said, that is, for

        if((int)ans.size() < depth){
            ans.push_back(root->val);
        }

This condition determines why you need to add an (int) strong conversion type. Because the type returned by ans.size() is an unsigned integer, the comparison between negative numbers and unsigned integers will always make mistakes, that is, the if judgment condition here is always false, and if cannot be entered.

/**
 * 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:
    void dfs(vector<int>& ans, TreeNode* root, int depth){
        if(root == nullptr){return;}
        if(ans.size() > depth){
            ans.push_back(root->val);
        }
        dfs(ans, root->right, depth + 1);
        dfs(ans, root->left, depth + 1);
    }
    vector<int> rightSideView(TreeNode* root) {
        vector<int> ans;
        dfs(ans, root, -1);
        return ans;
    }
};

The above is the original version that has been wrong. On the one hand, the roles of size and depth are not fully understood. On the other hand, this condition must seem correct, but it is always wrong due to type problems.

Posted by sxiix on Sat, 20 Nov 2021 19:28:59 -0800