【问题标题】:Are vectors in c++ so slow?c++中的向量这么慢吗?
【发布时间】:2012-10-10 01:47:49
【问题描述】:

我正在这里制作一个解决这个问题的程序:http://opc.iarcs.org.in/index.php/problems/BOOKLIST

我使用向量的唯一地方是:

for(int i =0;i < total_books; i++){
    int temp;
    cin >> temp;
    books_order.push_back(temp);
}

for(int i = 0;i < total_entries; i++){
    int index;
    cin >> index;
    index--;
    cout << books_order[index] << endl;
    books_order.erase(books_order.begin()+index);
}

(这是我的完整代码:http://cpaste.org/1377/

我在向量中使用的唯一函数是vector::erase、vector::push_back 和vector::begin。 对于大型输入,我的代码花费的时间超过 3 秒(这是该问题的时间限制),但是当我删除向量函数时,它运行得更快(但当然给出了错误的答案)

我知道在这个问题中使用向量是错误的,我的算法很慢,但我不明白为什么它很慢。

如果您知道为什么我使用的功能很慢,请向我解释。 谢谢。

【问题讨论】:

  • 任何编程语言都可能被滥用......这不是向量的错。
  • std::vector 实际上是一个数组,当需要更多空间时移动到新数组。如果您的向量必须以非常高的速度扩展,请尝试使用链接列表。
  • 您要解决的实际问题是什么?我不愿再鼓励使用此代码,但也许您会对不同的解决方案感兴趣。
  • 我看不出你的代码是如何解决这个问题的。问题中没有书籍,您确定链接正确吗?要解决您链接到的问题,您可能需要std::make_heapstd::push_heapstd::pop_heap
  • 问题链接和代码无关。那将是第一个问题。我要指出的是,您需要一种更适合您的问题的不同数据结构,但事实是首先您需要了解您需要解决的问题:)

标签: c++ algorithm vector


【解决方案1】:

我从向量中使用的唯一函数是vector::erase、vector::push_back 和vector::begin

我会说这是你的问题。 vector::eraseO(n),其他都是固定时间,应该不会造成在线判断问题的效率问题。避免使用擦除方法。

但是,一般来说,如果您事先知道数组的大小,我会使用一个简单的数组。如果可以,请不要增加任何不必要的开销。

要解决您链接到的问题,请考虑使用set:它的擦除方法是O(log n),它的插入方法也是如此。如果您需要随机删除,通常应该使用这种方法。

编辑:我忘记了 C++ 也有 priority queues,所以也看看这些。由于您只关心这里的最大值,它可能比集合更快,尽管它们都具有相同的理论复杂性(堆可以检索O(1) 中的最小值/最大值,但删除它,对于这个问题,你必须做的是O(log n)),在这种情况下,任何一个都可以正常工作。

【讨论】:

  • 谢谢大家的回答,从现在开始我会避免使用类似于vector::erase的方法。
  • @A.06:这不是避免给定方法的问题,而是知道与容器相关联的方法的复杂性是什么,以便您可以为正确的容器选择正确的容器任务。
  • 对于这个问题,我建议std::list 超过std::vectorstd::set。插入有序列表的成本是 O(log n),因为这是搜索有序列表的成本。插入本身是 O(1)。该 O(log n) 成本与将元素添加到 std::set 的成本相同。另一方面,从std::list 中删除第一个或最后一个元素(或任何元素)是 O(1)。
  • @DavidHammen 列表不起作用。你需要有一个数据结构来保持你的元素排序,这样你就可以在摆脱当前元素后获得下一个最大值。您无法通过列表有效地做到这一点。
  • @DavidHammen - 插入有序列表 O(n),而不是 O(log n)。
【解决方案2】:

IMO 的罪魁祸首是vector::erase,因为它会在移除元素后移动所有元素。所以除非你总是删除最后一个元素,否则它可能会很慢。

【讨论】:

    【解决方案3】:

    除非您调用reserve 将向量的大小调整为您拥有的元素数量,否则每次新大小超过向量容量时,调用push_back 都会重新分配内存。考虑使用reserve 为您的元素分配足够的内存,您应该会看到性能提升,尤其是在向量很大的情况下。还要注意有关使用erase 的其他答案。

    【讨论】:

      【解决方案4】:

      您的问题是对“擦除”的调用。如果您查看文档here,您会注意到:

      因为向量保持数组格式,擦除向量末端以外的位置也会将擦除段后的所有元素移动到它们的新位置,这可能不如在其他类型的序列容器(deque , 列表)。

      要么从末尾运行循环,要么在循环后擦除。

      【讨论】:

        【解决方案5】:

        从向量中间删除元素(使用vector::erase)很慢。这是因为必须向下移动向量的所有较高元素以填充您已删除的元素留下的空白。

        所以这不是向量变慢的情况,而是你的算法变慢了,因为你选择了不合适的数据结构。

        【讨论】:

          【解决方案6】:

          如果您事先知道向量中有一定数量的元素,您可以预留足够的空间来容纳所有内容,以节省向量内空间的重新分配:

          books_order.reserve(total_books)
          for(int i=0 ;i < total_books; ++i){
              int temp;
              cin >> temp;
              books_order.push_back(temp);
          }
          

          但这不是这里的问题,我敢肯定,如果您分析您的代码,您会看到 .erase() 使用带来的大量开销。从向量中间移除元素很慢,因为向量是如何实现的,它需要移动所有元素的一部分,即被移除的元素。考虑改用其他数据结构,例如 std::array

          【讨论】:

            【解决方案7】:

            你真的需要一个向量来实现你正在做的事情吗?

            您必须牢记向量的工作原理:它们本质上是动态数组,当您在其中添加更多项目时,它们会调整大小。调整向量的大小是一个 O(n) 操作(其中 n 是向量中的项目数),因为它会分配新内存并将项目复制到新向量。

            你可以在here阅读了解哪些向量擅长。

            我建议改用标准数组——这似乎完全合理,因为您已经知道总共有多少元素(即total_books

            【讨论】:

            • 你让它听起来比现在更糟。大多数时候,用向量替换数组不会对在线法官造成任何效率问题,因为插入向量仍然是摊销O(1)时间。这里的问题是数组AND向量数据结构不适合这个问题。
            • 我需要矢量,因为矢量擦除功能,这正是我需要解决的问题
            • @A.06 - 不,不是。看我的回答,用一套。
            猜你喜欢
            • 2023-04-10
            • 1970-01-01
            • 2021-08-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-09-04
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多