Лучший способ извлечь подвектор из вектора?

Предположим, у меня есть std::vector (назовем его myVec) размера N. Какой самый простой способ построить новый вектор, состоящий из копии элементов с X по Y, где 0 <= X <= Y <= N-1? Например, от myVec [100000] до myVec [100999] в векторе размером 150000.

Если это не может быть эффективно сделано с вектором, есть ли другой тип данных STL, который я должен использовать вместо этого?

вы говорите, что хотите извлечь подвектор, но мне кажется, что на самом деле вам нужно представление / доступ к подвектору - разница в том, что представление не будет копироваться - старый школьный C++ будет использовать начальный указатель и конечный указатель, учитывая тот факт, что mem в std :: vector является смежным, тогда у вас должна быть возможность выполнять итерацию с использованием указателей и, таким образом, избегать копирования, однако, если вы не против копирования, просто инициализируйте новый вектор с областью действия вашего предыдущего вектор

serup 13.02.2017 16:18

Начиная с C++ 11 существует .data () (cplusplus.com/reference/vector/vector/data). Однако использование указателей в контейнерах stl не рекомендуется, см. stackoverflow.com/questions/31663770/…

David Tóth 06.11.2019 09:46
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
321
2
370 051
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Вы можете использовать Копия STL с производительностью O (M), когда M - размер подвектора.

Проголосовали за, потому что он указал мне в правильном направлении, но я понимаю, почему @LokiAstari предлагает неправильный выбор - поскольку STL :: copy работает с двумя массивами std :: vector <T> одинакового размера и типа. Здесь OP хочет скопировать подраздел в новый, меньший массив, как описано здесь в сообщении OP: «0 <= X <= Y <= N-1»

Andrew 01.01.2017 02:22

@Andrew, см. Пример с использованием std :: copy и std :: back_inserter

chrisg 26.07.2017 09:03

@LokiAstari, почему бы и нет?

chrisg 26.07.2017 09:04

@chrisg: разгадка кроется в имени copy. Чтобы использовать std :: copy, вам нужно сначала создать место назначения (что означает инициализацию всех членов их значением по умолчанию). Затем вы можете скопировать их. Итак, это две разные операции. Почему бы просто не использовать конструктор вектора и не инициализировать члены непосредственно их конечными значениями. Посмотрите красивый ответ, за который проголосовало +50.

Martin York 26.07.2017 11:03

@LokiAstari Я имел в виду правку, которая не выдержала экспертной оценки, в которой приведен пример <br/> vector <T> newvec; std :: copy (myvec.begin () + 10000, myvec.begin () +10100, std :: back_inserter (newvec)); <br/> в этом случае вам не нужно сначала создавать место назначения, но, конечно, прямая инициализация более ... прямая.

chrisg 26.07.2017 22:16

@chrisg: Это тоже две строки. Кроме того, вам нужно вставить третью строку, чтобы убедиться, что она эффективна. newvec.reserve(10100 - 10000);. Это определенно вариант, и технически он будет работать. Но какой из двух вы порекомендуете?

Martin York 27.07.2017 00:11

@LokiAstari а, хорошо, я продана. Потребность в резерве на больших сетах подтолкнула меня к черту. Мне также не нравилось ощущение "под капотом" использования голых указателей, зависимости от базовой детали (или "гарантии", что вектор непрерывен) и неконтролируемого доступа, подобного массиву, потому что они кажутся очень похожими на C и не -С ++ - вроде, но потом я решил, что это не хуже, чем арифметика итератора, используемая для подхода std :: copy

chrisg 27.07.2017 01:14

@chrisg: в ответе, получившем наибольшее количество голосов, используются стандартные итераторы и конструктор.

Martin York 27.07.2017 19:00

std::vector<T>(input_iterator, input_iterator), в вашем случае foo = std::vector<T>(myVec.begin () + 100000, myVec.begin () + 150000);, см. Например здесь

Поскольку Эндрю пытается построить новый вектор, я бы рекомендовал "std :: vector foo (..." вместо копирования с помощью "foo = std :: vector (..."

Drew Dormann 07.01.2009 22:34

Да, конечно, но не имеет значения, набираете ли вы std :: vector <int> foo = std :: vector (...) или std :: vector <int> foo (...).

Anteru 07.01.2009 23:06
Ответ принят как подходящий
vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
vector<T> newVec(first, last);

Это операция O (N) для создания нового вектора, но на самом деле лучшего способа нет.

+1, также это O (Y-X), что меньше или равно O (N) (а в его примере намного меньше)

orip 08.01.2009 00:53

@orip Ну, тогда все-таки О (Н).

Johann Gerell 08.01.2009 10:56

@GregRogers: Нет смысла использовать нотацию большого O, где N - конкретное число. Big-O сообщает скорость роста относительно того, как изменяется N. Иоганн: Лучше не использовать одно имя переменной двумя способами. Обычно мы говорим либо O(Y-X), либо O(Z) where Z=Y-X.

Mooing Duck 11.09.2013 21:51

@GregRogers Используя этот способ, нам нужно объявить новый вектор. Есть ли способ изменить исходный вектор? что-то вроде myVec (первый, последний)? Я знаю, что это неправильно, но мне действительно нужно решение, поскольку я хочу использовать рекурсию в своих кодах, и мне нужно многократно использовать один и тот же вектор (хотя и измененный). Спасибо!

ulyssis2 05.11.2015 15:24

@ ulyssis2 Создать новый вектор и поменять местами?

AturSams 27.01.2016 18:07

Почему не только vector<T> newVec(myVec.begin() + 100000, myVec.begin() + 101000);?

aquirdturtle 21.03.2017 02:57

Кроме того, если newVec уже существует и вы хотите заменить его содержимое, используйте newVec.asign(first, last). Он имеет ту же семантику, что и конструктор в ответе.

Elvis Teixeira 07.06.2017 14:33

что произойдет, если последний итератор выйдет за пределы границ?

Dr Deo 20.08.2017 14:54

Вы также можете использовать auto вместо vector<T>::const_iterator

Ahmed Karaman 14.12.2017 10:50

Просто для моей справки, результирующий вектор - это неглубокая копия, верно? субвектор не является указателем на исходные векторы, лежащие в основе структуры данных, верно? Я в основном хочу знать, что если я вернусь к стеку, внутренние указатели на результирующий субвектор останутся действительными, потому что они отделены от исходного супер-вектора.

searchengine27 20.02.2018 02:57

Строка 3 - копия? Можно ли таким образом построить ссылочный подвектор?

Sandburg 05.10.2018 12:56

В качестве побочного примечания конструктор диапазона std :: vector constructs the container with the contents of the range [first, last).

AZTECCO 28.02.2020 13:27

Отличное предложение. Можно ли использовать auto cbegin вместо begin? auto first = myVec.cbegin() + 100000; auto last = myVec.cbegin() + 101000;

pamplemousse_mk2 01.03.2021 13:33

Единственный способ спроецировать коллекцию, которая не является линейной по времени, - это делать это лениво, когда результирующий «вектор» на самом деле является подтипом, который делегируется исходной коллекции. Например, метод Scala List#subseq создает подпоследовательность за постоянное время. Однако это работает только в том случае, если сборка неизменна и если базовый язык поддерживает сборку мусора.

в С ++ способ сделать это будет иметь вектор shared_ptr в X вместо вектора X, а затем скопировать SP, но, к сожалению, я не думаю, что это быстрее, потому что атомарная операция связана с cpying SP. Или исходный вектор может быть const shared_ptr of vector, и вы просто берете в нем ссылку на поддиапазон. конечно, вам не нужно делать его shared_ptr вектора, но тогда у вас есть проблемы на время жизни ... все это не в моей голове, может быть неправильно ...

NoSenseEtAl 22.10.2013 18:30

Просто используйте конструктор векторов.

std::vector<int>   data();
// Load Z elements into data so that Z > Y > X

std::vector<int>   sub(&data[100000],&data[101000]);

Хорошо, я не понимал, что получить итератор из произвольного векторного элемента так просто.

An̲̳̳drew 07.01.2009 22:58

Что ж, вы фактически получаете указатели на элементы, которые в большинстве случаев эквивалентны итераторам. Однако я предполагаю, что они не будут проверяться как итераторы, если у вас есть реализация проверенных итераторов. Может быть какая-то магия, о которой я не подозреваю, хотя это тоже помогает ...

Niklas 07.01.2009 23:56

Получение адреса этих векторных элементов - это непереносимый взлом, который сломается, если хранилище векторов на самом деле не является непрерывным. Используйте begin () + 100000 и т. д.

j_random_hacker 08.01.2009 09:29

Мое плохое, по-видимому, стандарт гарантирует непрерывность векторного хранилища. Тем не менее, работать с такими адресами - плохая практика, так как это, конечно, не гарантирует работы для всех контейнеров, поддерживающих произвольный доступ, в то время как begin () + 100000 работает.

j_random_hacker 08.01.2009 09:36

@j_random_hacker: Извините, что не согласен. Спецификация STL для std :: vector была явно изменена для поддержки этого типа процедуры. Также указатель является допустимым типом итератора. Найдите iterator_traits <>

Martin York 08.01.2009 10:35

Да, указатели не обязательно являются действительными std :: vector <T> :: iterator, но они являются действительными итераторами для массива в непрерывном хранилище, как оно и используется.

Greg Rogers 08.01.2009 23:04

@Uri: Это всегда будет работать. Но, как и весь код, он ограничен системными ограничениями. Если вы попытаетесь сделать что-то, что физически невозможно, вы получите исключение.

Martin York 17.08.2015 21:12

Как бы вы с помощью этого разрезали последний элемент? Если вы сделаете sub (& data [100000], & data [data.size ()]); Это не всегда будет работать и может вызвать нарушение прав доступа?

taktak004 01.10.2016 02:19

@ taktak004 Нет. Помните, что operator[] возвращает ссылку. Только в том месте, где вы читаете или записываете ссылку, это может стать нарушением доступа. Поскольку мы не делаем ни того, ни другого, а вместо этого получаем адрес, который мы не вызывали, UB ,.

Martin York 01.10.2016 04:24

Это не сработает, если вам нужно скопировать данные до конца исходного вектора.

bialix 06.12.2017 20:58

@bialix: Будет. Я предполагаю, что вы говорите о том, что взяли адрес одного из прошлых, а затем в конце концов стали проблемой. Это предусмотрено стандартом и разрешено. UB разыменовывать эти адреса, но не брать его адрес. Обратите внимание, что определение X[Y] определено в стандарте как *(X + Y). И операторы &*, помещенные вместе, компенсируют друг друга (при условии, что они не были переопределены (что не может быть для указателей)). => &X[Y] => &*(X + Y) => (X + Y). Вот почему 10000[data] является допустимым выражением для доступа к массиву.

Martin York 07.12.2017 00:35

Если оба не собираются изменять (не нужно добавлять / удалять элементы - изменение существующих - это нормально, если вы обращаете внимание на проблемы потоковой передачи), вы можете просто передать data.begin() + 100000 и data.begin() + 101000 и представить, что они являются begin() и end() из меньший вектор.

Или, поскольку векторное хранилище гарантированно непрерывно, вы можете просто передать массив из 1000 элементов:

T *arrayOfT = &data[0] + 100000;
size_t arrayOfTLength = 1000;

Оба эти метода требуют постоянного времени, но требуют, чтобы длина данных не увеличивалась, вызывая перераспределение.

Это также хорошо, если вы хотите, чтобы исходный вектор и подвектор были связаны.

PyRulez 01.03.2015 22:38

Публикация так поздно только для других .. Держу пари, что первый кодировщик уже готов. Для простых типов данных копирование не требуется, просто вернитесь к старым добрым методам кода C.

std::vector <int>   myVec;
int *p;
// Add some data here and set start, then
p=myVec.data()+start;

Затем передайте указатель p и len всем, кому нужен подвектор.

notelen должно быть !! len < myVec.size()-start

Копирование не выполняется.

Trilarion 30.01.2017 11:32

Вы не упомянули, какой тип std::vector<...> myVec, но если это простой тип или структура / класс, который не включает указатели, и вам нужна максимальная эффективность, вы можете сделать прямое копирование в память (что, я думаю, будет быстрее, чем другие предоставленные ответы). Вот общий пример для std::vector<type> myVec, где type в данном случае - это int:

typedef int type; //choose your custom type/struct/class
int iFirst = 100000; //first index to copy
int iLast = 101000; //last index + 1
int iLen = iLast - iFirst;
std::vector<type> newVec;
newVec.resize(iLen); //pre-allocate the space needed to write the data directly
memcpy(&newVec[0], &myVec[iFirst], iLen*sizeof(type)); //write directly to destination buffer from source buffer

Интересно, если с -O3, @ Anteru "using constructor" std::vector(myVec.begin () + 100000, myVec.begin () + 150000);, не будет ли более длинная версия этого преобразована в точно такую ​​же сборку?

sandthorn 14.02.2018 18:58

Например, MSVC++ 2015 компилирует std::vector<>(iter, iter) в memmove(), если это необходимо (если конструктор тривиальный, для подходящего определения тривиального).

Pablo H 26.06.2018 22:18

Не звоните memcpy. Создайте std::copy или конструктор, который принимает диапазон (два итератора), и компилятор и std.library сговорились вызвать memcpy, когда это необходимо.

Bulletmagnet 12.04.2019 11:38

Возможно, array_view / диапазон в библиотеке GSL - хороший вариант.

Вот также реализация одного файла: array_view.

Пожалуйста, добавьте сюда ответ вместе со ссылкой. Поскольку внешняя ссылка может измениться в будущем

Panther 27.04.2017 11:03

В наши дни мы используем span! Итак, вы бы написали:

#include <gsl/span>

...
auto start_pos = 100000;
auto length = 1000;
auto span_of_myvec = gsl::make_span(myvec);
auto my_subspan = span_of_myvec.subspan(start_pos, length);

чтобы получить диапазон из 1000 элементов того же типа, что и myvec. Или более сжатая форма:

auto my_subspan = gsl::make_span(myvec).subspan(1000000, 1000);

(но мне это не очень нравится, поскольку значение каждого числового аргумента не совсем понятно; и становится хуже, если length и start_pos имеют одинаковый порядок величины.)

В любом случае, помните, что это не копия, это просто вид данных в векторе, поэтому будьте осторожны. Если вам нужна настоящая копия, вы можете сделать:

std::vector<T> new_vec(my_subspan.cbegin(), my_subspan.cend());

Заметки:

использовал бы cbegin и cend только по принципу;) даже std::cbegin и т. д.

JHBonarius 29.06.2018 23:16

@JHBonarius: Видя, что этот код не основан на шаблоне выбора контейнера, я не вижу особого преимущества; полагаю, дело вкуса.

einpoklum 30.06.2018 00:16

Легко копируйте элементы из одного вектора в другой
В этом примере я использую вектор пар, чтобы упростить понимание
`

vector<pair<int, int> > v(n);

//we want half of elements in vector a and another half in vector b
vector<pair<lli, lli> > a(v.begin(),v.begin()+n/2);
vector<pair<lli, lli> > b(v.begin()+n/2, v.end());


//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
//then a = [(1, 2), (2, 3)]
//and b = [(3, 4), (4, 5), (5, 6)]

//if v = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
//then a = [(1, 2), (2, 3), (3, 4)]
//and b = [(4, 5), (5, 6), (6, 7)]

'
Как видите, вы можете легко копировать элементы из одного вектора в другой, если вы хотите скопировать элементы, например, из индекса 10 в 16, тогда мы будем использовать

vector<pair<int, int> > a(v.begin()+10, v.begin+16);

и если вам нужны элементы с индекса 10 до некоторого индекса с конца, тогда в этом случае

vector<pair<int, int> > a(v.begin()+10, v.end()-5);

надеюсь, это поможет, просто помните в последнем случае v.end()-5 > v.begin()+10

Еще один вариант: Полезно, например, при переходе между thrust::device_vector и thrust::host_vector, когда вы не можете использовать конструктор.

std::vector<T> newVector;
newVector.reserve(1000);
std::copy_n(&vec[100000], 1000, std::back_inserter(newVector));

Также должна быть сложность O (N)

Вы можете комбинировать это с верхним кодом ответа

vector<T>::const_iterator first = myVec.begin() + 100000;
vector<T>::const_iterator last = myVec.begin() + 101000;
std::copy(first, last, std::back_inserter(newVector));

Вы можете просто использовать insert

vector<type> myVec { n_elements };

vector<type> newVec;

newVec.insert(newVec.begin(), myVec.begin() + X, myVec.begin() + Y);

Это довольно старое обсуждение, но самое простое еще не упомянуто, с список-инициализация:

 vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2}; 

Требуется C++ 11 или выше.

Пример использования:

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

int main(){

    vector<int> big_vector = {5,12,4,6,7,8,9,9,31,1,1,5,76,78,8};
    vector<int> subvector = {big_vector.begin() + 3, big_vector.end() - 2};

    cout << "Big vector: ";
    for_each(big_vector.begin(), big_vector.end(),[](int number){cout << number << ";";});
    cout << endl << "Subvector: ";
    for_each(subvector.begin(), subvector.end(),[](int number){cout << number << ";";});
    cout << endl;
}

Результат:

Big vector: 5;12;4;6;7;8;9;9;31;1;1;5;76;78;8;
Subvector: 6;7;8;9;9;31;1;1;5;76;

Другие вопросы по теме