Я программист на C / Python на C++, впервые работаю с STL.
В Python для расширения списка другим списком используется метод .extend:
>>> v = [1, 2, 3]
>>> v_prime = [4, 5, 6]
>>> v.extend(v_prime)
>>> print(v)
[1, 2, 3, 4, 5, 6]
В настоящее время я использую этот алгоритмический подход для расширения векторов в C++:
v.resize(v.size() + v_prime.size());
copy(v_prime.begin(), v_prime.end(), v.rbegin());
Это канонический способ расширения векторов или есть более простой способ, который мне не хватает?





От здесь
// reserve() is optional - just to improve performance
v.reserve(v.size() + distance(v_prime.begin(),v_prime.end()));
v.insert(v.end(),v_prime.begin(),v_prime.end());
Я не думаю, что есть специализация vector :: insert для итераторов ввода произвольного доступа, поэтому, если производительность имеет значение, сначала резервируйте ().
И VC++ 9.0, и GCC 4.3.2 определяют категорию итератора внутренне, поэтому резервировать не нужно.
Я знаю, что ему 8 лет, но есть ли причина, по которой вы использовали distance() вместо просто v_prime.size()?
@Holt Он, вероятно, думал о более общем случае, когда вы хотите расширить вектор на некоторый диапазон элементов. Если вы, например, не хотите использовать все элементы из v_prime, вы можете просто использовать этот код и заменить итераторы.
copy(v_prime.begin(), v_prime.end(), back_inserter(v));
Я думаю, что пространство все еще должно быть зарезервировано () - d для повышения производительности
+1, так как спрашивающий просил «самый простой», а не «самый быстрый», поэтому резервировать место (хотя стоит упомянуть как вариант) не нужно.
Я думаю, что решение Дмитрия и проще, и быстрее. Проголосовать за этого парня сейчас :)
Этот ответ не следует поощрять. См. «Эффективный STL», пункт 5: Слишком много программистов на STL злоупотребляют copy, поэтому совет, который я только что дал, повторяется: почти все случаи использования copy, когда диапазон назначения указывается с помощью итератора вставки, следует заменить вызовами функций-членов диапазона.
@Chris, таким образом побеждая архитектуру STL.
Мне понадобились два разных варианта функции extend в C++ 14, один из которых поддерживал семантику перемещения для каждого добавляемого элемента вектора.
vec - это ваш v, а ext - это ваш v_prime.
/**
* Extend a vector with elements, without destroying source one.
*/
template<typename T>
void vector_extend(std::vector<T> &vec, const std::vector<T> &ext) {
vec.reserve(vec.size() + ext.size());
vec.insert(std::end(vec), std::begin(ext), std::end(ext));
}
/**
* Extend a vector with elements with move semantics.
*/
template<typename T>
void vector_extend(std::vector<T> &vec, std::vector<T> &&ext) {
if (vec.empty()) {
vec = std::move(ext);
}
else {
vec.reserve(vec.size() + ext.size());
std::move(std::begin(ext), std::end(ext), std::back_inserter(vec));
ext.clear();
}
}
Есть несколько способов достичь поставленной цели.
std::vector::insert
Вектор может быть расширен путем вставки новых элементов перед элементом в указанной позиции, эффективно увеличивая размер контейнера на количество вставленных элементов. Вы можете следовать одному из следующих подходов. Вторая версия использует C++ 11, и ее можно рассматривать как более общий ответ, поскольку b также может быть массивом.
a.insert(a.end(), b.begin(), b.end());
a.insert(std::end(a), std::begin(b), std::end(b));
Иногда при использовании рекомендуется использовать функцию резерва перед использованием std :: vector :: insert. Функция std :: vector :: резерв увеличивает емкость контейнера до значения, которое больше или равно new_cap. Если new_cap больше текущей емкости (), выделяется новое хранилище, в противном случае метод ничего не делает.
a.reserve(a.size() + distance(b.begin(), b.end()));
Использование резервной функции не требуется, но может быть рекомендовано. И лучше всего использовать резерв, если вы многократно вставляете в вектор, для которого вы знаете окончательный размер, и этот размер большой. В противном случае лучше позволить STL увеличивать ваш вектор по мере необходимости.
std::copy
std :: copy - второй вариант, который вы можете рассмотреть для достижения своей цели. Эта функция копирует элементы диапазона (first, last) в диапазон, начинающийся с result.
std::copy (b.begin(), b.end(), std::back_inserter(a));
Однако использование std :: copy медленнее, чем использование std :: vector :: insert (), потому что std :: copy () не может заранее зарезервировать достаточно места (у него нет доступа к самому вектору, только для итератора, у которого есть), тогда как std :: vector :: insert (), будучи функцией-членом, может. Из-за этого std :: copy действительно медленнее, чем использование std :: vector :: insert. Большинство людей чрезмерно используют std :: copy, не зная об этом сценарии.
boost::push_back
Третий вариант, который вы можете рассмотреть, - это использование функции boost отталкивать.
boost::push_back(a, b);
Использование std::vector::insert;
A.reserve(A.size() + B.size());
A.insert(A.end(), B.begin(), B.end());
reserve() не является обязательным, но его использование помогает повысить производительность.
Удобный генератор кода для экономии драгоценных секунд:
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/css/materialize.min.css"><script src = "https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.0/js/materialize.min.js"></script><script src = "https://cdn.jsdelivr.net/clipboard.js/1.6.0/clipboard.min.js"></script><script>function generateCode(){codeTemplate = "{0}.reserve({0}.size() + {1}.size()); \n{0}.insert({0}.end(), {1}.begin(), {1}.end());",first=document.getElementById("1").value,second=document.getElementById("2").value,""==first&&(first = "A"),""==second&&(second = "B"),document.getElementById("c").innerHTML=String.format(codeTemplate,first,second)}String.format||(String.format=function(a){var b=Array.prototype.slice.call(arguments,1);return a.replace(/{(\d+)}/g,function(a,c){return"undefined"!=typeof b[c]?b[c]:a})});</script><div class = "A" style = "margin:3% 10% 1% 10%;"><label for = "1">First vector name:</label><input id = "1"/><br/><label for = "1">Second vector name:</label><input id = "2"/><div class = "D"><a class = "waves-effect waves-light btn red col" onclick = "generateCode();" style = "margin:0 0 4% 0;">Generate Code</a></div><textarea id = "c" onclick = "this.select()" style = "border:none;height:auto;overflow: hidden;font-family:Consolas,Monaco;">A.reserve(A.size() + B.size()); A.insert(A.end(), B.begin(), B.end());</textarea></div>Это копия ответа stackoverflow.com/a/313444/931303, получившего наибольшее количество голосов, 11 лет спустя. Рассмотрите возможность его удаления.
Используйте только следующий синтаксис:
a.insert(a.end(), b.begin(), b.end());
Резерв / изменение размера не следует использовать, если вы не знаете, что делаете.
Резервирование может вызвать огромные накладные расходы, поскольку оно не обязательно приводит к экспоненциальному увеличению размера, поэтому каждый резерв может вызывать время O (n).
Это может быть не очень дорого, если выполнить Только один раз, и в таких случаях может оказаться более эффективным с точки зрения времени \ памяти. С другой стороны, если вы продолжите расширять массив таким образом с относительно меньшими массивами, это докажет, что очень сильно неэффективен. В следующем примере показано простое неправильное использование, которое приводит к увеличению времени в 10 000 раз ?
пример:
#include <vector>
#include <iostream>
#include <chrono>
int main() {
std::vector<int> a, b(50);
auto t1 = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 5e4; i++) {
a.reserve(a.size() + b.size()); // line in question.
a.insert(a.end(), b.begin(), b.end());
}
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>( t2 - t1 ).count();
std::cout << 1.0 * duration / 1e9;
return 0;
}
//run time complexity speed up
//with reserve 114.558 s O(N) x1
//without reserve 0.012 s O(N^2) x10000 (~O(N/50))
Скомпилировано с -O3 на gcc 17, intel i5.
Этот ответ мог бы лучше объяснить, что происходит. Без ручного резервирования пространства, т. Е. Позволяя вектору управлять своим пространством, стоимость выделения памяти амортизированный равна O (1). Это достигается за счет экспоненциального распределения памяти. При ручном резервировании размер выделения памяти в этом примере является линейным (еще 50 элементов в каждом распределении), что приводит к затратам O (n) на выделение памяти. Конечно, O (1) быстрее, чем O (n).
Возможный дубликат Объединение двух std :: vectors