【问题标题】:Insert multiple elements at multiple offsets into an vector将多个偏移量处的多个元素插入向量中
【发布时间】:2019-07-13 12:06:07
【问题描述】:

我有一个 vector<uint32_t> values 和一个 vector<std::pair<uint32_t, uint_32_t>> updates ,其中包含(大量)对向量的累积更新,我想尽可能便宜地实施。

{5, 12} 的更新应在values[5] 之后插入12不涉及任何其他修改,即values = {10,20,30}updates = {{0,5}, {1, 6}, {2, 7}} 应导致values = {10, 5, 20, 6, 30, 7}

我想像这样根据updates向量修改向量:

static void update(std::vector<uint32_t>& values,
    std::vector<std::pair<uint32_t, uint32_t>> updates) {
    std::sort(updates.begin(), updates.end(),
        [](std::pair<uint32_t, uint32_t> a, std::pair<uint32_t, uint32_t> b){
             return a.first < b.first;
        });
    values.reserve(values.size()+updates.size());
    for(uint32_t i = 0; i < updates.size(); ++i){
        values.insert(values.begin()+i+updates[i].first, updates[i].second);
    }

}

如果我允许重复的update[i].first,我需要使用std::stable_sort 来保持相对顺序。

显然,这段代码很慢,使用O(n^2) time 一次将向量的其余部分移回一个。应该有更好的解决方案。

已经有一个关于 SO 的问题,非常相似:Insert multiple values into vector。虽然有一个答案可以用来在O(1) 空间和O(n) 时间更新我的向量,但使用 c++03 的问题已经很老了,我想知道是否有一种现代方法可以做到这一点(或甚至,如果我可以避免事先致电std::sort)。

【问题讨论】:

  • 可能是错的,但没有排序就不一样了?我看到了两个问题:a)您应该扩展向量而不仅仅是保留,b)您应该根据 max updates.first 值扩展而不是更新大小
  • 不排序我不知道之前的更新是否移动了我的索引@DimChtz 编辑:为什么我应该扩展 max(updates.first)?每次调用insert 都会将我的values 增加一个元素
  • updates[i].first 是什么索引?因为现在看起来应用更新的顺序很重要。 (5,12) 是指在当前vec[5] 处插入值 12,还是在所有更新之前插入值 12,或者在一些更新之后但在其他更新之前插入值,或者在所有更新之后插入值 vec[5]=12
  • @Quimby:编辑(希望)清楚。
  • 从 c++11/C++14 中获得的主要是auto,以避免写冗长的std::vector&lt;/**/&gt;::/*reverse_*/iterator。算法本身不会改变。

标签: c++ c++11 vector c++14


【解决方案1】:

也许这样的事情应该可行。由于我们知道所有更新,我们知道每个值必须移动多少才能为新值腾出空间。也就是说,恰好等于给定值具有较低索引的更新数量。我们可以从后面开始,将值移动|updates| 位置,插入索引最高的更新,将下一批移动|updates-1| 位置,插入第二高的更新...

static void update(std::vector<uint32_t>& values,
    std::vector<std::pair<uint32_t, uint32_t>> updates) {
    std::sort(updates.begin(), updates.end(),[](auto a, auto b){
             return a.first > b.first;//From highest to lowest indices.
        });

    const std::size_t N = values.size();
    std::size_t K = updates.size();
    values.resize(N+K);
    std::size_t end = N;
    for(auto [i,v]:updates){
        //Shift the values in [i+1,end) K positions right
        std::move_backward(values.begin()+i+1,
                           values.begin()+end,
                           values.begin()+end+K);
        //Insert the update
        values[i+K]=v;
        //Preceding values are shifted only by K-1 positions
        --K;
        //Already shifted values
        end=i+1;
    }
}

这需要O(u log u) 对更新排序,O(u+n) 移动旧值并添加新值。只完成了一个resize。请注意,resize 将添加的值初始化为零,原始数组在这里会稍微更有效。或者使用emplace_back 做一些索引魔术。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-05
    • 2020-08-11
    • 1970-01-01
    • 2018-07-23
    • 2021-07-24
    • 2017-12-31
    相关资源
    最近更新 更多