【问题标题】:Inserting into a vector at the front在前面插入向量
【发布时间】:2010-11-19 15:44:33
【问题描述】:
iterator insert ( iterator position, const T& x );

std::Vector类的插入操作符的函数声明。

这个函数的返回类型是一个指向被插入元素的迭代器。我的问题是,考虑到这种返回类型,什么是最有效的方式(这是我正在运行的一个更大程序的一部分,其中速度至关重要,所以我正在寻找 计算效率最高的方式) 在开头插入。是以下吗?

//Code 1
vector<int> intvector;
vector<int>::iterator it;
it = myvector.begin();
for(int i = 1; i <= 100000; i++){
    it = intvector.insert(it,i);
}

或者,

//Code 2
vector<int> intvector;
for(int i = 1; i <= 100000; i++){
    intvector.insert(intvector.begin(),i);
}

本质上,在代码 2 中,是参数,

intvector.begin() 

与在代码 1 中使用返回的迭代器相比,计算“成本高”还是应该同样便宜/昂贵?

【问题讨论】:

  • 添加到向量的前面意味着将所有其他元素向后移动。如果您想(不断执行)前插入,您可能真的想使用listdeque
  • 了解如何加速程序的唯一方法是分析。您应该先编程,然后对其进行分析并找出答案。别猜了。做第二个,它更干净。 (显然,这些微优化永远不会产生影响;例如,您应该使用可以更快地插入到前面的容器,例如 deque。)
  • 您也可以使用 IgushArray (github.com/igushev/IgushArray),它像数组一样具有快速的常量时间访问操作,但插入/擦除操作只需 O (N^1/2) 时间。注意,结构对reserve()非常敏感
  • 有一个版本的vector::insert 将一个范围(两个迭代器)作为参数而不是一个值。获取一个迭代器(最好是随机访问,但至少向前)来生成要添加的整数,然后调用一次插入:这样,向量将进行一次重新分配+洗牌,为所有新值腾出空间.
  • std::vector::insert() 可以使您的迭代器无效,因为它可能会导致向量调整大小。

标签: c++ performance vector


【解决方案1】:

如果您的程序的关键需求之一是在容器的开头插入元素:那么您应该使用std::deque 而不是std::vectorstd::vector 只擅长在末尾插入元素。

在 C++11 中引入了其他容器。我应该开始使用这些新容器查找更新后的图表并将其插入此处。

【讨论】:

  • 使用list 的唯一情况是当您需要splice 成员时。请改用deque
  • 不只是。根据这个图,在中间插入时我们应该使用std::list。
  • @Billy -- 这不是使用列表的唯一情况。如果您需要频繁地从列表中间插入或删除元素,或者调用需要这些操作的函数(如拼接或排序),则使用列表
  • @PigBen:排序实际上会在列表上变慢,因为排序实际上并没有删除任何东西——它只需要比较和交换。 (好吧,如果您的对象很大,交换操作可以稍微便宜一些,但这是一个不正常的用例)“在中间插入”参数的问题是到达列表的中间需要线性时间。如果你可以让迭代器指向你需要的地方,那么一定要使用 list.但是,我认为我从未见过有人为此目的使用列表。通常需要的是双端队列。
  • 看起来原版在这里...? linuxsoftware.co.nz/cppcontainers.html
【解决方案2】:

获取插入点的效率一点也不重要 - 每次插入时都会不断地将现有数据打乱,效率低下,这将相形见绌。

为此使用 std::deque,这就是它的设计目的。

【讨论】:

    【解决方案3】:

    一个旧线程,但它作为 Google 查询的第一个搜索结果出现在同事的办公桌上。

    除了使用双端队列之外,还有一种值得考虑的选择:

    std::vector<T> foo;
    for (int i = 0; i < 100000; ++i)
      foo.push_back(T());
    std::reverse( foo.begin(), foo.end() );
    

    你仍然使用一个比 deque 更加工程化的向量来提高性能。此外,交换(这是反向使用的)非常有效。另一方面,复杂度仍然是线性的,但增加了 50%。

    与往常一样,在决定做什么之前进行衡量。

    【讨论】:

    • 人们说双端队列,但问题是向量。这个回复,恕我直言,最适合
    【解决方案4】:

    deque 很可能是其他人建议的适当解决方案。但是为了完整起见,假设您只需要进行一次前端插入,程序中的其他地方您不需要在前端进行其他操作,否则vector 提供了您需要的接口。如果所有这些都是真的,您可以使用非常有效的 push_back 添加项目,然后添加 reverse 向量以使所有内容井井有条。这将具有线性复杂性,而不是像在前面插入时那样的多项式。

    【讨论】:

    • +1 以获得问题的解决方案。也许稍后在代码中拥有一个向量很重要。此外,使用reserve 已经预留内存并节省不断重新预留的开销可能会更糟。
    【解决方案5】:

    如果您正在寻找一种计算效率高的在前面插入的方法,那么您可能希望使用双端队列而不是向量。

    【讨论】:

    • 谢谢。我会研究双端队列。我希望像vector类一样,deque类也允许通过object[index]访问它的元素。在我的应用程序中,插入是问题的一部分......访问元素是另一个问题。
    • @Tryer,是的,双端队列支持通过[] 表示法进行快速访问。不如矢量快,但足以满足大多数用途。
    【解决方案6】:

    当您使用向量时,您通常会知道它将包含的实际元素数量。在这种情况下,保留所需数量的元素(在您展示的情况下为 100000)并使用 [] 运算符填充它们是最快的方法。如果您确实需要在前面进行高效插入,可以使用dequelist,具体取决于您的算法。

    您还可以考虑反转算法的逻辑并在末尾插入,这通常对于向量来说更快。

    【讨论】:

    • Operator[] 并不比任何与迭代器相关的方法快,或者实际上是任何其他填充容器的方法(即std::generate_n)。保留并不能真正解决这里发生的事情。是的,你节省了一点分配,但你仍然得到了 n 平方的整体运算。
    • @Bill:是的,操作符并不比迭代器或生成函数快。我只是指出,在开始时保留所需的空间(如果他或她知道它有多少)然后通过操作员访问它也可以节省时间并且通常使算法更清晰。但同样,取决于算法。
    【解决方案7】:

    如果您真的想在开头插入数据,我认为您应该更改容器的类型。这就是vector没有push_front()成员函数的原因。

    【讨论】:

      【解决方案8】:

      直觉上,我同意@Happy Green Kid Naps 的观点,并进行了一个小测试,表明对于小尺寸(原始数据类型的 1 std::deque 似乎比反转 std::vector 具有更高的性能。因此,在您决定之前进行基准测试。另一个因素可能是容器的元素类型。

      • 测试 1:push_front (a) 1std::deque
      • 测试 2:push_back (a) 1std::vector 后跟 std::reverse

      结果:

      • 测试 1 - 双端队列 (a) 19 µs
      • 测试 2 - 矢量 (a) 19 µs
      • 测试 1 - 双端队列 (b) 6339 µs
      • 测试 2 - 向量 (b) 10588 µs

      【讨论】:

        【解决方案9】:

        你可以支持-

        1. 在前面插入。
        2. 在末尾插入。
        3. 在任何位置更改值(不会出现在双端队列中)
        4. 在任何索引处访问值(不会出现在双端队列中)

        以上所有操作的时间复杂度为 O(1)

        注意:你只需要知道 max_size 的上限,它可以左右进入。

        class Vector{
        public:
            int front,end;
            int arr[100100];   // you should set this in according to 2*max_size
            Vector(int initialize){
                arr[100100/2] = initialize; // initializing value 
                front = end = 100100/2;     
                front--;end++;
            }
            void push_back(int val){
                arr[end] = val;
                end++;
            }
            void push_front(int val){
                if(front<0){return;} // you should set initial size accordingly
                arr[front] = val;
                front--;
            }
            int value(int idx){
                return arr[front+idx];
            }
            // similarity create function to change on any index
        };
        
        int main(){
            Vector v(2);
            for(int i=1;i<100;i++){
                // O(1)
                v.push_front(i);
            }
        
            for(int i=0;i<20;i++){
                // to access the value in O(1)
                cout<<v.value(i)<<" ";
            }
            return;
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多