【问题标题】:Insert multiple values into vector将多个值插入向量
【发布时间】:2013-08-15 16:01:40
【问题描述】:

我有一个std::vector<T> 变量。我还有两个 T 类型的变量,第一个代表向量中要插入的值,第二个代表要插入的值。

假设我有这个容器:1,2,1,1,2,2

根据上面的定义,这两个值分别是 2 和 3。然后我想写一个函数来更新容器,而不是包含:

1,2,3,1,1,2,3,2,3

我正在使用 c++98 和 boost。我可以使用哪些 std 或 boost 函数来实现此功能?

遍历向量并使用 std::insert 是一种方法,但是当您意识到您需要记住跳过刚刚插入的值时,它会变得混乱。

【问题讨论】:

  • 你知道这种带有 std::vector 的算法的效率问题吗?每次插入一个值时,都会复制向量的其余部分。因为 std::list 可能不是一个好的解决方案,所以我可以想到一个只移动一次元素的就地算法。但不知道Boost中是否存在。
  • 我没有谈到重新分配向量。元素之间没有多余的空格,向量的主要特征之一是具有连续的数据。当然,如果您总是在末尾插入,则不会出现问题。好吧,请参阅我对就地工作的算法的回答。
  • @PierreT.:对不起,我不明白你在说什么。是的,您想像 Benjamin Lindley 的回答所示那样执行此操作:将数据放入一个新向量中,以便您始终在最后添加数据,然后将内容交换回原始向量。
  • @JerryCoffin:嗯,正如我所说,我有一个解决方案,它不使用双倍内存,只复制一次元素(就像在 Benjamin 的回答中一样)。
  • @JerryCoffin 我的答案是看不见的还是你不想看?

标签: c++ boost c++98


【解决方案1】:

这是我可能会做的:

vector<T> copy;
for (vector<T>::iterator i=original.begin(); i!=original.end(); ++i)
{
    copy.push_back(*i);
    if (*i == first)
        copy.push_back(second);
}
original.swap(copy);

如果需要,请致电预订。您知道您至少需要空间来容纳 original.size() 元素。您也可以对向量进行初始迭代(或使用std::count)来确定要保留的元素的确切数量,但如果不进行测试,我不知道这是否会提高性能。

【讨论】:

  • 我会 reserve() 原来的内存量,否则这看起来不错。如果我被要求提高性能,我还会尝试将元素从一个容器交换/移动到另一个容器。
【解决方案2】:

我提出了一个解决方案,该解决方案在内存中的 O(n) 和 O(2n) 时间内有效。 Laethnes 提出的解决方案在时间上不是 O(n^2),而 Benjamin 提出的解决方案在内存中是 O(2n)。

// First pass, count elements equal to first.
std::size_t elems = std::count(data.begin(), data.end(), first);
// Resize so we'll add without reallocating the elements.
data.resize(data.size() + elems);
vector<T>::reverse_iterator end = data.rbegin() + elems;
// Iterate from the end. Move elements from the end to the new end (and so elements to insert will have some place).
for(vector<T>::reverse_iterator new_end = data.rbegin(); end != data.rend() && elems > 0; ++new_end,++end)
{
  // If the current element is the one we search, insert second first. (We iterate from the end).
  if(*end == first)
  {
    *new_end = second;
    ++new_end;
    --elems;
  }
  // Copy the data to the end.
  *new_end = *end;
}

这个算法可能有问题,但想法是每个元素只复制一次:

  1. 首先计算我们需要插入多少元素。
  2. 其次,从末尾遍历数据并将每个元素移动到新的末尾。

【讨论】:

  • data.resize(data.size() + elems);不比向量结果好;结果.reserve(...); ... original.swap(结果)。我认为首先计算结果大小很好
  • 为什么不呢?我使用了一半的内存。如果 original 是 10Mb 向量,我将只使用 10Mb。如果使用临时向量,则将使用 20Mb。
  • @Pierre T.: C++ 没有 realloc
  • 视情况而定。如果原始向量有足够的容量,则不需要额外的空间。如果没有,那么在将元素复制到新缓冲区时,对调整大小的调用将需要足够的空间来保持原始缓冲区处于活动状态。在我的算法中,总是需要额外的空间。
  • 如果 resize() 改变了向量 capacity() 它将使您刚刚保存的 end 迭代器无效。这段代码不好。
【解决方案3】:

这就是我可能会做的:

typedef ::std::vector<int> MyList;
typedef MyList::iterator MyListIter;

MyList data;

// ... fill data ...

const int searchValue = 2;
const int addValue = 3;

// Find first occurence of searched value
MyListIter iter = ::std::find(data.begin(), data.end(), searchValue);

while(iter != data.end())
{
    // We want to add our value after searched one
    ++iter;

    // Insert value and return iterator pointing to the inserted position
    // (original iterator is invalid now).
    iter = data.insert(iter, addValue);

    // This is needed only if we want to be sure that out value won't be used
    // - for example if searchValue == addValue is true, code would create
    // infinite loop.
    ++iter;

    // Search for next value.
    iter = ::std::find(iter, data.end(), searchValue);
}

但正如您所见,我无法避免您提到的增量。但我不认为这是一件坏事:我会将这段代码放在单独的函数中(可能在某种“核心/实用程序”模块中)并且 - 当然 - 将此函数实现为模板,所以我只会编写它一次 - 恕我直言,只有一次担心增加价值是可以接受的。非常可以接受。

template <class ValueType>
void insertAfter(::std::vector<ValueType> &io_data,
                 const ValueType &i_searchValue,
                 const ValueType &i_insertAfterValue);

甚至更好(恕我直言)

template <class ListType, class ValueType>
void insertAfter(ListType &io_data,
                 const ValueType &i_searchValue,
                 const ValueType &i_insertAfterValue);

编辑:

好吧,我会以不同的方式解决问题:首先计算搜索值出现的次数(最好存储在某种可以重复保存和使用的缓存中),这样我就可以在之前准备数组(只有一次分配)并使用memcpy 移动原始值(当然,仅适用于 int 等类型)或 memmove(如果向量分配的大小已经足够)。

【讨论】:

  • @DieterLücking:糟糕——我没有仔细阅读。我撤回了我之前的评论——它完全不正确。
  • Benjamin Lindley 解决方案更适合矢量,您的解决方案更适合列表。因此,两个模板都实施了不同的策略。
【解决方案4】:

就地,O(1) 额外内存和 O(n) 时间 (Live at Coliru):

template <typename T, typename A>
void do_thing(std::vector<T, A>& vec, T target, T inserted) {
    using std::swap;

    typedef typename std::vector<T, A>::size_type size_t;
    const size_t occurrences = std::count(vec.begin(), vec.end(), target);
    if (occurrences == 0) return;

    const size_t original_size = vec.size();
    vec.resize(original_size + occurrences, inserted);

    for(size_t i = original_size - 1, end = i + occurrences; i > 0; --i, --end) {
        if (vec[i] == target) {
            --end;
        }
        swap(vec[i], vec[end]);
    }
}

【讨论】:

    猜你喜欢
    • 2021-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 1970-01-01
    相关资源
    最近更新 更多