不抄袭的班级成员

Posted

技术标签:

【中文标题】不抄袭的班级成员【英文标题】:Class member without copying 【发布时间】:2019-12-13 03:36:33 【问题描述】:

我正在尝试为https://www.leetcode.com 上的编码挑战编写解决方案。 leetcode 解决方案的入口点是一个名为Solution 的类的方法。他们控制方法的名称,每个问题都不同。

我正在研究一种有点复杂的方法来解决问题,为了管理复杂性,我编写了很多辅助函数。大多数辅助函数都需要一些相同的变量,例如 vector<int> 我必须搜索一些东西。

我想避免将这些变量传递给我编写的每个辅助函数。我试图通过使它们成为Solution 的成员变量来做到这一点。我通常使用ints 之类的原始变量来做到这一点,这不会影响空间复杂度,但我只是尝试使用向量来做到这一点,其中副本确实会影响空间复杂度我尝试坚持使用恒定空间解决方案。

如何将向量存储为类的成员变量而不复制它,最好是不使用指针?我不想更改我的代码来处理指针。

这是一个寻找数组最小值和最大值的假装问题的最小示例。如果我给出问题的解决方案,他们会通过将测试用例传递给Solution().findMaximumAndMinimum(testCase); 来测试我的程序。

class Solution 
    vector<int> numbers;

    pair<int, int> findMaximumAndMinimum(vector<int>& A) 
        numbers = A;
        return findMinimum(), findMaximum();
    

    int findMaximum() 
        auto maximum = numbers.front();
        for (auto number : numbers) 
            if (maximum < number) 
                maximum = number;
            
        

        return maximum;
    

    int findMinimum() 
        auto minimum = numbers.front();
        for (auto number : numbers) 
            if (minimum > number) 
                minimum = number;
            
        

        return minimum;
    
;

我要更改的是 sn-p 的第 5 行:numbers = A。这将导致A 被复制到numbers。我不希望复制发生:我正在努力制作一个尽可能少占用时间和空间的解决方案,而复制我正在搜索的向量肯定会占用大量空间。

总结:我正在尝试制作一个看起来有点像上面的解决方案,但针对的是一个不同且更难的问题。上面的 sn-p 在显示我想要的内容时故意做的很简单。我想避免将A 明确传递给Solution 的其他方法。

背景:

我最初尝试将向量转换为引用成员vector&lt;int&gt;&amp; numbers,但构造函数遇到了问题。我相信在这种情况下我无法使用默认构造函数,因此它被报告为“已删除”,正如this answer 所说。 Leetcode 将使用默认构造函数调用我的类。我无法控制。 也许我可以在Solution 中放置一个类并改用这个类,在那里我可以控制类的对象是如何构造的,因此引用成员变量不会造成麻烦,但我不想诉诸于此。我不希望我的程序在开始时缩进两次。

PS:欢迎提出如何不通过引用一个类的许多方法来绕过A的建议。请记住约束:我的解决方案的入口点是一个名为Solution 的类的方法,我无法控制该方法的名称。 不欢迎提出解决上述问题的不同方法的建议,因为以上是人为的 MWE

【问题讨论】:

通过引用每个成员传递向量,或者更好的是,完全摆脱这些成员,只需在您的 findMaximumAndMinimum 成员中使用单遍循环来查找 both 极值。 std::vector 就像你写的那样一个成员变量,你已经通过引用传递它,使用vector&lt;int&gt;&amp;。我不明白你的问题。 @Frontear 该行将传递的参数复制给成员。没有自引用分配。然后numbers 用于计算每个成员函数中的极值。 @WhozCraig 正如我在帖子中所说,我实际上并没有试图解决这个最小和最大问题。这个关于极值的假装问题只是我对我正在寻找的最小示例的尝试。我还说我试图避免将数组传递给每个函数。我正在研究的问题有点复杂,我喜欢在编程时使用许多命名函数来描述我在做什么;必须一遍又一遍地传递一个明显的变量会使它变得混乱。 @Frontear 是的,向量是成员变量。那不是我的麻烦。它不是某种形式的对另一个向量的引用。据我了解,我的 sn-p 的第 5 行将导致 A 被复制到 numbers。我不想将A 复制到数字,我希望numbers 引用A。这是一个每一个小副本都很重要的情况,这是一场竞争。因为这是一场比赛,所以我有这样的限制,即我的程序的入口点是一个类的方法。 【参考方案1】:

这是我的问题的一种可能的答案,但我想避免。如果我可以控制Solution 的构造方式,那么作为引用的成员变量就不是问题了。所以我创建了__actualSolution 一个类,我可以完全控制它的构造方式,然后在 leetcode 知道的类中使用__actualSolution。但是……这看起来很复杂,很难理解。我问这个问题的目的是尽可能让一个复杂的程序保持可读性。

class __actualSolution 
    private:
        vector<int>& numbers;
        __actualSolution(vector<int>& A) : numbers(A) 

    public:
        static pair<int, int> findMaximumAndMinimum(vector<int>& A) 
            auto s = __actualSolution(A);
            return s.findMinimum(), s.findMaximum();
        

        int findMaximum() 
            auto maximum = numbers.front();
            for (auto number : numbers) 
                if (maximum < number) 
                    maximum = number;
                
            

            return maximum;
        

        int findMinimum() 
            auto minimum = numbers.front();
            for (auto number : numbers) 
                if (minimum > number) 
                    minimum = number;
                
            

            return minimum;
        
;

class Solution 
    public:
        pair<int, int> findMaximumAndMinimum(vector<int>& A) 
            return __actualSolution::findMaximumAndMinimum(A);
        

我希望从进一步的答案中避免类似上述的事情。帮助我坚持一个类,并避免将A 传递给Solution 的每个方法,请记住,我正在处理的实际问题比这两个人为的方法有更多的帮助函数。

【讨论】:

【参考方案2】:

假设你主要关心的是

如何将向量存储为类的成员变量而不复制它,最好不使用指针?

那么std::move() 可能适合您的需要。但是,为此,您必须同意将您的函数参数视为右值,并因此将其资源“窃取”而不是复制。

如果是这种情况,您可以按照以下步骤进行。

class Solution 
public:
    typedef std::pair<int, int> solution;
private:
    std::vector<int> numbers;
    solution s =  INT_MAX, -INT_MAX ;
    int& min = s.first;
    int& max = s.second;
public:
    const solution& findMaximumAndMinimum(std::vector<int>& A) 
        numbers = std::move(A);
        min = max = numbers[0];
        for (const auto& value : numbers) 
            if      (value < min) min = value;
            else if (value > max) max = value;
        
        return s;
    
;

虽然 Leetcode 提交不包含 main(),但我在此处添加它是为了说明行为。

int main() 
    std::vector<int> testCase =  4,7,6,1,5,2 ;

    std::cout << "testCase size before 'move()' = " << testCase.size() << std::endl;
    
    Solution::solution s = Solution().findMaximumAndMinimum(testCase);

    std::cout << "testCase size after 'move()' = " << testCase.size() << std::endl;
    std::cout << "Solution = " << s.first << ", " << s.second << "";
    return 0;

输出:

//testCase size before 'move()' = 6
//testCase size after 'move()' = 0
//Solution = 1, 7

自 C++11 起,STL 容器支持移动语义。 除其他外,这意味着每当您制作 STL容器之间的赋值操作AND通知编译器 rhs 参数(也是一个 STL 容器)可以被视为一个可移动的右值,那么 rhs 值将被移动而不是复制。在一个情况下 大小为 n 的向量,而不是提升 n 个副本,这种分配“窃取”了 rhs 关联的指针及其缓冲区 尺寸。 这非常快,特别是在处理大向量时,因为复杂度从 O(n) 下降到 O(1)。

您可以在自己的类中嵌入移动语义,但这超出了本问题的范围。如果您想深入了解,请参考this text 作为起点。

【讨论】:

一个leetcode提交不包括main,也不能控制main,只能控制class SolutionfindMaximumAndMinimum 之类的测试函数将使用左值调用,因此不能将参数更改为右值引用。 哦,非常感谢您指出这一点。我不熟悉 leetcode 及其限制。我已经编辑了我的答案以反映您的评论。现在,参数是一个左值,所有的处理都在函数内完成。我的测试都成功了。请让我知道我的 Solution 课程现在是否符合 Leetcode。再次感谢。 @RonaldChiesse 谢谢。我对破坏原作不高兴,但我认为这通常不会成为问题。这应该有助于解决许多 LC 问题。

以上是关于不抄袭的班级成员的主要内容,如果未能解决你的问题,请参考以下文章

线程消息或更好:访问“大师班”中其他班级的成员

访问另一个班级中的一个班级成员[关闭]

我可以将班级成员的哈希码用于班级吗?

Proguard 不会保留班级成员的枚举

列为班级成员

如何列出我班级中所有成员的特定属性(Python 3.5.3)