Построить вектор векторов в C++
В этом посте представлен обзор доступных альтернатив для построения вектора векторов в C++.
В C++ мы можем определить вектор векторов целых чисел следующим образом:
Приведенное выше определение приводит к пустому двумерному вектору. Чтобы использовать его, мы должны определить размер вектора и выделить память для его элементов. Существует несколько методов выращивания двумерного вектора с помощью resize() или же push_back() функций или с помощью конструктора заполнения или списков инициализаторов. Теперь давайте подробно рассмотрим каждую альтернативу:
1. Использование resize() функция
The resize() Функция используется для изменения размера вектора до указанного размера. Мы можем использовать его для инициализации вектора векторов, как показано ниже:
вствка вектора векторов в вектор векторов
Как вставить вектор векторов в вектор векторов после k -того элемента.То есть если у нас есть вектор векторов 10 на 10, то нужно будет вставить второй вектор векторов 5 на 10 чтобы вышло 15 на 10.
Отслеживать
задан 18 апр 2017 в 19:26
1,049 3 3 золотых знака 14 14 серебряных знаков 29 29 бронзовых знаков
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Нужно использовать метод insert класса std::vector , принимающий диапазон значений, заданных с помощью входных итераторов.
Ниже, в демонстрационной программе показано, как это можно сделать.
#include #include #include int main() < std::vector> v1(10, std::vector( 10, 0 )); std::vector> v2(5, std::vector( 10, 1 )); size_t k = 5; for ( const auto &row : v1 ) < for ( int x : row ) std::cout std::cout << std::endl; v1.insert( std::next( v1.begin(), k ), v2.begin(), v2.end() ); for ( const auto &row : v1 ) < for ( int x : row ) std::cout std::cout
Вывод программы на консоль
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
В общем случае вам надо проверять, что значение переменной k не превышает значения, возвращаемого функцией-членом класса std::vector size . В противном случае в качестве позиции вставки вместо выражения std::next( v1.begin(), k ) вам следует использовать выражение v1.end() .
Урок №95. std::vector (векторы)
На предыдущем уроке мы рассматривали std::array, который является более безопасной и удобной формой обычных фиксированных массивов в языке C++. Аналогично, в Стандартной библиотеке C++ есть и улучшенная версия динамических массивов (более безопасная и удобная) — std::vector.
В отличие от std::array, который недалеко отходит от базового функционала обычных фиксированных массивов, std::vector идет в комплекте с дополнительными возможностями, которые делают его одним из самых полезных и универсальных инструментов в языке C++.
Оглавление:
Векторы
Представленный в C++03, std::vector (или просто «вектор») — это тот же динамический массив, но который может сам управлять выделенной себе памятью. Это означает, что вы можете создавать массивы, длина которых задается во время выполнения, без использования операторов new и delete (явного указания выделения и освобождения памяти). std::vector находится в заголовочном файле vector. Объявление std::vector следующее:
// Нет необходимости указывать длину при инициализации
std :: vector < int >array ;
std :: vector < int >array2 = < 10 , 8 , 6 , 4 , 2 , 1 >; // используется список инициализаторов для инициализации массива
std :: vector < int >array3 < 10 , 8 , 6 , 4 , 2 , 1 >; // используется uniform-инициализация для инициализации массива (начиная с C++11)
Обратите внимание, что в неинициализированном, что в инициализированном случаях вам не нужно явно указывать длину массивов. Это связано с тем, что std::vector динамически выделяет память для своего содержимого по запросу.
Подобно std::array, доступ к элементам массива может выполняться как через оператор [] (который не выполняет проверку диапазона), так и через функцию at() (которая выполняет проверку диапазона):
array [ 7 ] = 3 ; // без проверки диапазона
array . at ( 8 ) = 4 ; // с проверкой диапазона
В любом случае, если вы будете запрашивать элемент, который находится вне диапазона array , длина вектора автоматически изменяться не будет. Начиная с C++11, вы также можете присваивать значения для std::vector, используя список инициализаторов:
array = < 0 , 2 , 4 , 5 , 7 >; // ок, длина array теперь 5
array = < 11 , 9 , 5 >; // ок, длина array теперь 3
В таком случае вектор будет самостоятельно изменять свою длину, чтобы соответствовать количеству предоставленных элементов.
Нет утечкам памяти!
Когда переменная-вектор выходит из области видимости, то она автоматически освобождает память, которую контролировала (занимала). Это не только удобно (так как вам не нужно это делать вручную), но также помогает предотвратить утечки памяти. Рассмотрим следующий фрагмент:
void doSomething ( bool value )
int * array = new int [ 7 ] < 12 , 10 , 8 , 6 , 4 , 2 , 1 >;
// Делаем что-нибудь
delete [ ] array ; // если value == true, то этот стейтмент никогда не выполнится
Если переменной value присвоить значение true , то array никогда не будет удален, память никогда не будет освобождена и произойдет утечка памяти.
Однако, если бы array был вектором, то подобное никогда бы и не произошло, так как память освобождалась бы автоматически при выходе array из области видимости (независимо от того, выйдет ли функция раньше из области видимости или нет). Именно из-за этого использование std::vector является более безопасным, чем динамическое выделение памяти через оператор new.
Длина векторов
В отличие от стандартных динамических массивов, которые не знают свою длину, std::vector свою длину запоминает. Чтобы её узнать, нужно использовать функцию size():
std :: vector < int >array < 12 , 10 , 8 , 6 , 4 , 2 , 1 >;
std :: cout << "The length is: " << array . size ( ) << '\n' ;
The length is: 7
Изменить длину стандартного динамически выделенного массива довольно проблематично и сложно. Изменить длину std::vector так же просто, как вызвать функцию resize():
std :: vector < int >array < 0 , 1 , 2 >;
array . resize ( 7 ) ; // изменяем длину array на 7
std :: cout << "The length is: " << array . size ( ) << '\n' ;
for ( auto const & element : array )
std :: cout << element << ' ' ;
The length is: 7
0 1 2 0 0 0 0
Здесь есть две вещи, на которые следует обратить внимание. Во-первых, когда мы изменили длину array , существующие значения элементов сохранились! Во-вторых, новые элементы были инициализированы значением по умолчанию в соответствие с определенным типом данных (значением 0 для типа int).
Длину вектора также можно изменить и в обратную сторону (обрезать):
std :: vector < int >array < 0 , 1 , 4 , 7 , 9 , 11 >;
array . resize ( 4 ) ; // изменяем длину array на 4
std :: cout << "The length is: " << array . size ( ) << '\n' ;
for ( auto const & element : array )
std :: cout << element << ' ' ;
The length is: 4
0 1 4 7
Изменение длины вектора является затратной операцией, поэтому вы должны стремиться минимизировать количество подобных выполняемых операций.
Заключение
Это вводная статья, предназначенная для ознакомления с основами std::vector. На следующих уроках мы детально рассмотрим std::vector, в том числе и разницу между длиной и ёмкостью вектора, и то, как в std::vector выполняется выделение памяти.
Поскольку переменные типа std::vector могут сами управлять выделенной себе памятью (что помогает предотвратить утечку памяти), отслеживают свою длину и легко её изменяют, то рекомендуется использовать std::vector вместо стандартных динамических массивов.
(475 оценок, среднее: 4,88 из 5)
Урок №94. Введение в std::array
Глава №6. Итоговый тест
Комментариев: 21
Есть ли разница в позиции ключевого слова const в объявлении цикла foreach или оба варианта эквивалентны и обозначают константную ссылку на текущий элемент массива?
for ( auto const & element : array )
std :: cout << element << ' ' ;
for ( const auto & element : array )
std :: cout << element << ' ' ;
Когда мы пытаемся работать с элементом массива вне диапазона то происходит ошибка времени выполнения, правильно? Ибо с этой ошибкой проект собирается(VS2019).
Поскольку ошибка возникает во время работы кода, а не на этапе компиляции, то это ошибка времени выполнение. При желании можно проверить это, запустив этот код:
std :: vector < int >array ;
array . resize ( 3 ) ;
system ( "pause" ) ;
std :: cout << array . at ( 4 ) ;
В данном случае код компилируется и начинает работу, и после объявления вектора выполнение кода ставиться на паузу
Я не очень поняла один момент, может, кто-то сможет подсказать, буду признательна)
В начале урока написано: "Начиная с C++11, вы также можете присваивать значения для std::vector, используя список инициализаторов … В таком случае вектор будет самостоятельно изменять свою длину, чтобы соответствовать количеству предоставленных элементов."
А в конце показывают функцию resize( ), которая делает то же самое, но при этом является затратной.
Так вооот, насколько затратным будет изменить длину вектора через список инициализаторов и зачем тогда нужна отдельная функция? Только для работы с крупными объемами данных, чтобы не переписывать их все вручную? Или есть ещё что-то?
Jaroshevskii :
Мне кажется что изменять длину std::vector что через метод resize(), что через список инициализаторов будет одинаково затратным (возможно в будущих уроках это будет более подробно описано). Отличие же resize() от списка инициализаторов в том что при вызове метода resize() он изменяет длину std::vector и при этом те элементы которые входят в диапазон размера будет сохранены без изменений.
Изменения размера с помощью списка инициализаторов изменит не только длину std::vector но и запишит вместо старых элементов новые (те которые были записаны в списке инициализаторов).
Юрий, вы восхитительны! У вас не только классные уроки, но и абсолютно замечательная система кросс-навигации между ними — на сайт изначально попала в поисках информации про классы. Пойду читать ваши уроки с самого начала, у меня хоть какая-то минимальная база знаний и есть, но всё равно так много новых и полезных штук открываю
Юрий :
Доброго времени суток!
В этой статье нет ни слова про дву-х, трё-х, *N — мерные векторы и массивы пространства имён std. Подозреваю что можно реализовать аналогично указателю на указатель (древовидная структура) массив в массиве, но надеюсь есть способ проще и удобней. Конечно сам доберусь до этого, но надеюсь получить ответ и возможно мой вопрос поможет улучшить/дополнить 95-й урок.
Это было вводное занятие, поверхностное обозрение темы, написано же в конце. Дальше, естественно, будет подробнее
Как добавить вектор в вектор c
Для добавления элементов в вектор применяется функция push_back() , в которую передается добавляемый элемент:
#include #include int main() < std::vectornumbers; // пустой вектор numbers.push_back(5); numbers.push_back(3); numbers.push_back(10); for(int n : numbers) cout << n << "\t"; // 5 3 10 std::cout
Векторы являются динамическими структурами в отличие от массивов, где мы скованы его заданым размером. Поэтому мы можем динамически добавлять в вектор новые данные.
Функция emplace_back() выполняет аналогичную задачу - добавляет элемент в конец контейнера:
std::vector numbers< 1, 2, 3, 4, 5 >; numbers.emplace_back(8); // numbers = < 1, 2, 3, 4, 5, 8 >;
Добавление элементов на определенную позицию
Ряд функций позволяет добавлять элементы на определенную позицию.
- emplace(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos
- insert(pos, value) : вставляет элемент value на позицию, на которую указывает итератор pos, аналогично функции emplace
- insert(pos, n, value) : вставляет n элементов value начиная с позиции, на которую указывает итератор pos
- insert(pos, begin, end) : вставляет начиная с позиции, на которую указывает итератор pos, элементы из другого контейнера из диапазона между итераторами begin и end
- insert(pos, values) : вставляет список значений начиная с позиции, на которую указывает итератор pos
std::vector numbers< 1, 2, 3, 4, 5 >; auto iter = numbers.cbegin(); // константный итератор указывает на первый элемент numbers.emplace(iter + 2, 8); // добавляем после второго элемента numbers = < 1, 2, 8, 3, 4, 5>;
std::vector numbers1< 1, 2, 3, 4, 5 >; auto iter1 = numbers1.cbegin(); // константный итератор указывает на первый элемент numbers1.insert(iter1 + 2, 8); // добавляем после второго элемента //numbers1 = < 1, 2, 8, 3, 4, 5>; std::vector numbers2 < 1, 2, 3, 4, 5 >; auto iter2 = numbers2.cbegin(); // константный итератор указывает на первый элемент numbers2.insert(iter2 + 1, 3, 4); // добавляем после первого элемента три четверки //numbers2 = < 1, 4, 4, 4, 2, 3, 4, 5>; std::vector values < 10, 20, 30, 40, 50 >; std::vector numbers3 < 1, 2, 3, 4, 5 >; auto iter3 = numbers3.cbegin(); // константный итератор указывает на первый элемент // добавляем после первого элемента три первых элемента из вектора values numbers3.insert(iter3 + 1, values.begin(), values.begin() + 3); //numbers3 = < 1, 10, 20, 30, 2, 3, 4, 5>; std::vector numbers4 < 1, 2, 3, 4, 5 >; auto iter4 = numbers4.cend(); // константный итератор указывает на позицию за последним элементом // добавляем в конец вектора numbers4 элементы из списка < 21, 22, 23 >numbers4.insert(iter4, < 21, 22, 23 >); //numbers4 = < 1, 2, 3, 4, 5, 21, 22, 23>;
Удаление элементов
Если необходимо удалить все элементы вектора, то можно использовать функцию clear :
std::vector v < 1,2,3,4 >; v.clear();
Функция pop_back() удаляет последний элемент вектора:
std::vector v < 1,2,3,4 >; v.pop_back(); // v =
Если нужно удалить элемент из середины или начала контейнера, применяется функция std::erase() , которая имеет следующие формы:
- erase(p) : удаляет элемент, на который указывает итератор p. Возвращает итератор на элемент, следующий после удаленного, или на конец контейнера, если удален последний элемент
- erase(begin, end) : удаляет элементы из диапазона, на начало и конец которого указывают итераторы begin и end. Возвращает итератор на элемент, следующий после последнего удаленного, или на конец контейнера, если удален последний элемент
std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; auto iter = numbers1.cbegin(); // указатель на первый элемент numbers1.erase(iter + 2); // удаляем третий элемент // numbers1 = < 1, 2, 4, 5, 6 >std::vector numbers2 = < 1, 2, 3, 4, 5, 6 >; auto begin = numbers2.cbegin(); // указатель на первый элемент auto end = numbers2.cend(); // указатель на последний элемент numbers2.erase(begin + 2, end - 1); // удаляем с третьего элемента до последнего // numbers2 =
Также начиная со стандарта С++20 в язык была добавлена функция std::erase() . Она не является частью типа vector. В качестве первого параметра она принимает вектор, а в качестве второго - элемент, который надо удалить:
std::vector numbers3 < 1, 2, 3, 1, 5, 6 >; std::erase(numbers3, 1); // numbers3 =
В данном случае удаляем из вектора numbers3 все вхождения числа 1.
Размер вектора
С помощью функции size() можно узнать размер вектора, а с помощью функции empty() проверить, путой ли вектор:
#include #include int main() < std::vectornumbers; if(numbers.empty()) std::cout
С помощью функции resize() можно изменить размер вектора. Эта функция имеет две формы:
- resize(n) : оставляет в векторе n первых элементов. Если вектор содержит больше элементов, то его размер усекается до n элементов. Если размер вектора меньше n, то добавляются недостающие элементы и инициализируются значением по умолчанию
- resize(n, value) : также оставляет в векторе n первых элементов. Если размер вектора меньше n, то добавляются недостающие элементы со значением value
std::vector numbers1 < 1, 2, 3, 4, 5, 6 >; numbers1.resize(4); // оставляем первые четыре элемента - numbers1 = numbers1.resize(6, 8); // numbers1 =
Важно учитывать, что применение функции resize может сделать некорректными все итераторы, указатели и ссылки на элементы.
Изменение элементов вектора
Функция assign() позволяет заменить все элементы вектора определенным набором:
std::vector langs = < "Java", "JavaScript", "C">; langs.assign(4, "C++"); // langs =
В данном случае элементы вектора заменяются набором из четырех строк "C++".
Также можно передать непосредственно набор значений, который заменит значения вектора:
std::vector langs< "Java", "JavaScript", "C">; langs.assign(< "C++", "C#", "C">); // langs =
Еще одна функция - swap() обменивает значения двух контейнеров:
std::vector clangs < "C++", "C#", "Java" >; std::vector ilangs < "JavaScript", "Python", "PHP">; clangs.swap(ilangs); // clangs = < "JavaScript", "Python", "PHP">; for(std::string lang : clangs)
Сравнение векторов
Векторы можно сравнивать - они поддерживают все операции сравнения: , =, ==, !=. Сравнение контейнеров осуществляется на основании сравнения пар элементов на тех же позициях. Векторы равны, если они содержат одинаковые элементы на тех же позициях. Иначе они не равны:
std::vector v1 ; std::vector v2 ; std::vector v3 ; bool v1v2 = v1 == v2; // true bool v1v3 = v1 != v3; // true bool v2v3 = v2 == v3; // false