Should C + + return an object or a pointer to the object when brushing questions?

Keywords: C++ Algorithm data structure leetcode

This paper only discusses whether to return the object or the pointer of the object when writing algorithm problems in C + +.

As we all know, C + + variables are different from java and python. C + + variables are not natural pointers, so the assignment between object type variables is much more time-consuming than java and python. For example, the following code:

//cpp
vector<int> a(10000);
vector<int> b = a;    // For copy construction, a and b vector s have their own independent space, which requires time and space

//python
a = [0 for _ in range(10000)]
b = a    # Address assignment assigns the address of object a to b. A and b point to the same list, which hardly consumes time and space

When writing algorithm problems in C + +, we occasionally encounter the scene of returning objects in functions. Is it better to return objects directly, or should we return a pointer to an object? I believe many people worry that the temporary construction of the directly returned object will consume a lot of time and cause the final TLE. So I did some experiments:

The problem to be solved is Leetcode 2003. Minimum gene value missing in each subtree , using the solution of violent merging (not AC, just for experiment), construct test cases on leetcode and codeforces to test (see the performance in extreme environments). For the test of leetcode platform, I did not use playground (because running code on playground does not show memory consumption), but used 779. The kth grammatical symbol Test by submitting code in the question (because the time and space required for AC in the question itself is almost 0, which will not cause too much time and space interference to the test). Use directly on codeforces custom test Testing is very convenient. In addition, the cost of actual problem-solving is tested by using AC heuristic merging method.

Results (average of three runs)

leetcode test (n=3210)codeforces test (n=3210)codeforces test (n=10000)leetcode actual problem solving
Return object (dfs1)1877ms / 384MB390 ms / 5476 KB3961 ms / 6932 KB1111ms / 397MB
Return object pointer (dfs2)1159ms / 339MB327 ms / 115880 KBMLE928ms / 416MB
Return object pointer and free memory (dfs3)1915ms / 384MB390 ms / 5436 KB3883 ms / 6992 KB1217ms / 436MB

The result is still very unexpected. It seems that the RVO (return value optimization) of C + + is really good. On the whole, the efficiency of returning object (dfs1) and returning object pointer and freeing memory (dfs3) is similar on the two platforms. However, the leetcode platform does not know what magical optimization the compiler has done. There is no need to manually release memory and MLE, and the time consumption is obviously better than the two normal writing methods. It seems that if you write questions on leetcode in the future, if you feel that the time complexity cannot be optimized, you can try to return the object pointer without releasing memory.

In addition, we can also see that the efficiency of codeforces is really good. Almost the same code and data run so much faster than leetcode. No wonder codeforces has raised many people with stl dependence hhh.

leetcode test code (submitted to 779. The kth grammatical symbol)

bool flag;
class S {
public:
    vector<int> res;
    vector<int> g[100005];
    unordered_set<int> dfs1(int now, vector<int>& nums)
    {
        unordered_set<int> se;
        se.insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs1(v, nums);
            // if (temp.size() > se.size()) swap(temp, se);
            for (auto e:temp) se.insert(e);
            res[now] = max(res[now], res[v]);
        }
        while (se.count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs2(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs2(v, nums);
            // if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            // delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs3(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs3(v, nums);
            // if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    vector<int> smallestMissingValueSubtree() {
        vector<int> parents(3210), nums(3210); //If it is 4000 direct TLE, no result can be obtained
        //Construct test data
        iota(parents.begin(), parents.end(), -1);
        iota(nums.begin(), nums.end(), 1);

        res.resize(parents.size(), 1);
        for (int i = 0; i < parents.size(); i++)
            if (parents[i] != -1)
                g[parents[i]].push_back(i);
        
        dfs1(0, nums);
        // dfs2(0, nums);
        // dfs3(0, nums);
        return {};
    }
};
class Solution {
public:
    int kthGrammar(int n, int k) {   
        if (!flag) //leetcode will call the function many times and get a global variable to ensure that it is executed only once, otherwise TLE
        {
            S().smallestMissingValueSubtree();
            flag = true;
        }
        return __builtin_popcount(k-1)%2;
    }
};

codeforces code (using custom test,GNU G++17 7.3.0)

#include <bits/stdc++.h>
using namespace std;

class S {
public:
    vector<int> res;
    vector<int> g[100005];
    unordered_set<int> dfs1(int now, vector<int>& nums)
    {
        unordered_set<int> se;
        se.insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs1(v, nums);
            // if (temp.size() > se.size()) swap(temp, se);
            for (auto e:temp) se.insert(e);
            res[now] = max(res[now], res[v]);
        }
        while (se.count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs2(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs2(v, nums);
            // if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            // delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs3(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs3(v, nums);
            // if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    vector<int> smallestMissingValueSubtree() {
        vector<int> parents(10000), nums(10000); 
        //Construct test data
        iota(parents.begin(), parents.end(), -1);
        iota(nums.begin(), nums.end(), 1);

        res.resize(parents.size(), 1);
        for (int i = 0; i < parents.size(); i++)
            if (parents[i] != -1)
                g[parents[i]].push_back(i);
        
        dfs1(0, nums);
        // dfs2(0, nums);
        // dfs3(0, nums);
        return {};
    }
};

int main()
{
	S().smallestMissingValueSubtree();
}

leetcode actual problem solving code (submitted to Leetcode 2003. Minimum gene value missing in each subtree)

class Solution {
public:
    vector<int> res;
    vector<int> g[100005];
    unordered_set<int> dfs1(int now, vector<int>& nums)
    {
        unordered_set<int> se;
        se.insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs1(v, nums);
            if (temp.size() > se.size()) swap(temp, se);
            for (auto e:temp) se.insert(e);
            res[now] = max(res[now], res[v]);
        }
        while (se.count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs2(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs2(v, nums);
            if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            // delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    unordered_set<int>* dfs3(int now, vector<int>& nums)
    {
        unordered_set<int>* se = new unordered_set<int>();
        se->insert(nums[now]);
        for (auto v : g[now])
        {
            auto temp = dfs3(v, nums);
            if (temp->size() > se->size()) swap(temp, se);
            for (auto e:(*temp)) se->insert(e);
            delete temp;
            res[now] = max(res[now], res[v]);
        }
        while (se->count(res[now])) ++res[now];
        return se;
    }
    vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
        res.resize(parents.size(), 1);
        for (int i = 0; i < parents.size(); i++)
            if (parents[i] != -1)
                g[parents[i]].push_back(i);
        
        dfs1(0, nums);
        // dfs2(0, nums);
        // dfs3(0, nums);
        return res;
    }
};

Posted by AnnieKay on Mon, 20 Sep 2021 23:04:34 -0700