【问题标题】: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。这是一个每一个小副本都很重要的情况,这是一场竞争。因为这是一场比赛,所以我有这样的限制,即我的程序的入口点是一个类的方法。

标签: c++


【解决方案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 Solution。 findMaximumAndMinimum 之类的测试函数将使用左值调用,因此不能将参数更改为右值引用。
    • 哦,非常感谢您指出这一点。我不熟悉 leetcode 及其限制。我已经编辑了我的答案以反映您的评论。现在,参数是一个左值,所有的处理都在函数内完成。我的测试都成功了。请让我知道我的 Solution 课程现在是否符合 Leetcode。再次感谢。
    • @RonaldChiesse 谢谢。我对破坏原作不高兴,但我认为这通常不会成为问题。这应该有助于解决许多 LC 问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-19
    • 1970-01-01
    • 2011-08-09
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 2012-01-15
    相关资源
    最近更新 更多