简体   繁体   中英

Insert multiple elements at multiple offsets into an vector

I've got an vector<uint32_t> values and an vector<std::pair<uint32_t, uint_32_t>> updates that contains (a lot) accumulated updates to the vector, which I want to enact as cheaply as possible.

An update of {5, 12} should insert 12 right after the the values[5] , not concerning any other modifications , ie values = {10,20,30} and updates = {{0,5}, {1, 6}, {2, 7}} should result in values = {10, 5, 20, 6, 30, 7} .

I want to modify the vector according to the updates vector like this:

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);
    }

}

If I'd allow duplicated update[i].first , I'd need to use std::stable_sort to keep the relative order.

Obviously, this code is quite slow, using O(n^2) time to move the remainder of the vector back one at a time. There should be a better solution.

There is a question on SO already, that is quite similar: Insert multiple values into vector . While there is an answer that I could use to update my vector in O(1) space and O(n) time, the question is quite old using c++03 and I wanted to know if there is a modern way to do that (or even, if I could avoid calling std::sort beforehand).

Perhaps something like this should work. Since we know all updates, we know how much must each value be shifted to make space for the new ones. That is, exactly the amount equal to the number of updates with lower indices that the given value has. we can work from backwards, shifting the values by |updates| positions, insert the update with the highest index, shift the next batch by |updates-1| positions, insert the second-highest update...

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;
    }
}

This needs O(u log u) to sort the updates and O(u+n) to shift the old and add the new values. Only one resize is done. Note that resize zero-initializes the added values, the raw array would be slightly more efficient here. Or do some index magic with emplace_back .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM