C++ 在特定位置向向量插入多个值
假设我有一个像这样的整数向量C++ 在特定位置向向量插入多个值,c++,vector,C++,Vector,假设我有一个像这样的整数向量std::vector\u data 我知道如果我想从\u data中删除多个项目,那么我只需调用 _data.erase( std::remove_if( _data.begin(), _data.end(), [condition] ), _data.end() ); 这比清除多个元素要快得多,因为向量中需要的数据移动更少。我想知道是否有类似的东西用于插入 例如,如果我有以下几对 auto pair1 = { _data.begin() + 5, 5 }; au
std::vector\u data代码>
我知道如果我想从\u data
中删除多个项目,那么我只需调用
_data.erase( std::remove_if( _data.begin(), _data.end(), [condition] ), _data.end() );
这比清除多个元素要快得多,因为向量中需要的数据移动更少。我想知道是否有类似的东西用于插入
例如,如果我有以下几对
auto pair1 = { _data.begin() + 5, 5 };
auto pair2 = { _data.begin() + 12, 12 };
我可以使用一些现有的std
函数在一次迭代中插入这两个吗?我知道我可以做一些事情,比如:
_data.insert( pair2.first, pair2.second );
_data.insert( pair1.first, pair1.second );
但是对于大向量(说100000+个元素)来说,这(非常)慢
编辑:基本上,我有一个自定义的集合(和映射),它使用向量
作为底层容器。我知道我可以只使用std::set
或std::map
,但我所做的遍历次数远远超过了插入/删除次数。从集合
和映射
切换到此自定义集合/映射已经减少了20%的运行时间。但目前,插入大约占剩余运行时间的10%,因此减少这一点很重要
不幸的是,订单也是必需的。我尽可能多地使用无序
版本,但在某些地方,顺序确实很重要 一种方法是创建另一个向量,其容量等于原始大小加上被插入元素的数量,然后执行插入循环,无需重新分配O(N)复杂度:
template<class T>
std::vector<T> insert_elements(std::vector<T> const& v, std::initializer_list<std::pair<std::size_t, T>> new_elements) {
std::vector<T> u;
u.reserve(v.size() + new_elements.size());
auto src = v.begin();
size_t copied = 0;
for(auto const& element : new_elements) {
auto to_copy = element.first - copied;
auto src_end = src + to_copy;
u.insert(u.end(), src, src_end);
src = src_end;
copied += to_copy;
u.push_back(element.second);
}
u.insert(u.end(), src, v.end());
return u;
}
int main() {
std::vector<int> v{1, 3, 5};
for(auto e : insert_elements(v, {{1,2}, {2,4}}))
std::cout << e << ' ';
std::cout << '\n';
}
好的,我们需要一些假设。让old_end
作为向量最后一个元素的反向迭代器。假设您的\u数据
已调整大小,以完全适合其当前内容和要插入的内容。假设inp
是一个std::pair
容器,其中包含按相反顺序插入的数据(因此首先插入最后面位置的元素,依此类推)。然后我们可以做:
std::merge(old_end,_data.rend(),inp.begin(),inp.end(),data.rend(),[int i=inp.size()-1](常数&T,常数&std::对p)可变{
如果(std::distance(_data.begin(),p.first)=i){
--一,;
返回false;
}
返回true;
}
但是,我认为这并不比使用一个好的for
更清楚。stl算法的问题是谓词作用于值而不是迭代器,这对于这个问题来说有点烦人。我的观点如下:
template<class Key, class Value>
class LinearSet
{
public:
using Node = std::pair<Key, Value>;
template<class F>
void insert_at_multiple(F&& f)
{
std::queue<Node> queue;
std::size_t index = 0;
for (auto it = _kvps.begin(); it != _kvps.end(); ++it)
{
// The container size is left untouched here, no iterator invalidation.
if (std::optional<Node> toInsert = f(index))
{
queue.push(*it);
*it = std::move(*toInsert);
}
else
{
++index;
// Replace current node with queued one.
if (!queue.empty())
{
queue.push(std::move(*it));
*it = std::move(queue.front());
queue.pop();
}
}
}
// We now have as many displaced items in the queue as were inserted,
// add them to the end.
while (!queue.empty())
{
_kvps.emplace_back(std::move(queue.front()));
queue.pop();
}
}
private:
std::vector<Node> _kvps;
};
同样,在索引0和1处插入意味着什么,这可能会造成混淆(是否会在两者之间插入一个原始元素?在第一个片段中,您会,在第二个片段中,您不会)。您可以在同一索引中多次插入吗?插入前索引有意义,插入后索引则没有意义。您也可以通过传递当前的*it
(即键值对)来编写此命令对函子来说,但这一点似乎并不太有用…这是我的一次尝试,它以相反的顺序插入。我确实去掉了用于此的迭代器/索引
template<class T>
void insert( std::vector<T> &vector, const std::vector<T> &values ) {
size_t last_index = vector.size() - 1;
vector.resize( vector.size() + values.size() ); // relies on T being default constructable
size_t move_position = vector.size() - 1;
size_t last_value_index = values.size() - 1;
size_t values_size = values.size();
bool isLastIndex = false;
while ( !isLastIndex && values_size ) {
if ( values[last_value_index] > vector[last_index] ) {
vector[move_position] = std::move( values[last_value_index--] );
--values_size;
} else {
isLastIndex = last_index == 0;
vector[move_position] = std::move( vector[last_index--] );
}
--move_position;
}
if ( isLastIndex && values_size ) {
while ( values_size ) {
vector[move_position--] = std::move( values[last_value_index--] );
--values_size;
}
}
}
模板
无效插入(标准::向量和向量,常量标准::向量和值){
size\t last\u index=vector.size()-1;
vector.resize(vector.size()+values.size());//依赖于T是默认可构造的
size\u t move\u position=vector.size()-1;
size\t last\u value\u index=values.size()-1;
size\u t values\u size=values.size();
bool islastinex=false;
while(!isLastIndex&&values\u size){
if(值[last_值\u索引]>向量[last_索引]){
向量[move_position]=std::move(值[last_value_index--]);
--大小值;
}否则{
isLastIndex=last_索引==0;
向量[move_position]=std::move(向量[last_index--]);
}
--移动位置;
}
if(islastinex&&value\u size){
while(值大小){
向量[move_position--]=std::move(值[last_value_index--]);
--大小值;
}
}
}
在Godbolt上使用ICC、Clang和GCC进行了尝试,vector的插入速度更快(对于插入的5个数字)。在我的机器MSVC上,结果相同,但不太严重。我还根据Maxim的回答将其版本与之进行了比较。我意识到使用Godbolt不是一种很好的比较方法,但我无法访问当前机器上的其他3个编译器
来自我的机器的结果:
我的插页:659us
马克西姆插页:712us
矢量插入:315us
戈博尔特国际刑事法院
我的插页:470us
马克西姆插页:139us
矢量插入:127us
Godbolt'sgc
我的插页:815us
Maxim insert:97us
矢量插入:97us
戈博尔特的叮当声:
我的插页:477us
马克西姆插页:188美元
矢量插入:96us
也许<代码>预留()/<代码>或<代码> Resie()/代码>会有帮助吗?向量的内部容量已经足够(预分配在200000左右),如果你在中间插入很多,那么你就不应该使用<代码> STD::向量< /代码>,但是其他的东西。什么是“其他东西”??这取决于实际的应用程序-您的代码解决的问题。因此,您的问题基本上受到影响。据我所知,没有标准的库算法。您可能必须自己编写。@胡桃,抱歉,只是一个示例。我已改变了顺序。我希望您的答案。我必须对使用迭代器而不是位置,或者我可以将它包装起来,然后使用距离。我发现了一种稍微好一点的方法,它不使用迭代器或位置,而是使用插入排序。编辑:我实际上只是将它与向量进行比较。插入
,两者都比向量
慢:(
template<class F>
void insert_at_multiple(F&& f)
{
std::queue<Node> queue;
std::size_t index = 0;
for (auto it = _kvps.begin(); it != _kvps.end(); ++it)
{
if (std::optional<Node> toInsert = f(index))
queue.push(std::move(*toInsert));
if (!queue.empty())
{
queue.push(std::move(*it));
*it = std::move(queue.front());
queue.pop();
}
++index;
}
// We now have as many displaced items in the queue as were inserted,
// add them to the end.
while (!queue.empty())
{
if (std::optional<Node> toInsert = f(index))
{
queue.push(std::move(*toInsert));
}
_kvps.emplace_back(std::move(queue.front()));
queue.pop();
++index;
}
}
template<class T>
void insert( std::vector<T> &vector, const std::vector<T> &values ) {
size_t last_index = vector.size() - 1;
vector.resize( vector.size() + values.size() ); // relies on T being default constructable
size_t move_position = vector.size() - 1;
size_t last_value_index = values.size() - 1;
size_t values_size = values.size();
bool isLastIndex = false;
while ( !isLastIndex && values_size ) {
if ( values[last_value_index] > vector[last_index] ) {
vector[move_position] = std::move( values[last_value_index--] );
--values_size;
} else {
isLastIndex = last_index == 0;
vector[move_position] = std::move( vector[last_index--] );
}
--move_position;
}
if ( isLastIndex && values_size ) {
while ( values_size ) {
vector[move_position--] = std::move( values[last_value_index--] );
--values_size;
}
}
}