Двумерные массивы
Для хранения прямоугольных таблиц, состоящих из строк и столбцов, необходимо использовать массивы, каждый элемент которых является строкой массивом чисел. Если считать, что одна строка — это vector , то двумерный массив — это вектор элементов типа vector , то есть его нужно объявлять как vector
Если объявить массив a таким образом, то a[i] будет одномерным массивом, который обычно считают строкой. То есть a[i][j] будет j-м элементом i-й строки. Например, двумерный массив из 3 строк и 4 столбцов можно записать в виде:
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
Чтобы создать массив из n строк и m столбцов, можно объявить его указанным образом:
vector > a;
Затем необходмио размер “внешнего” массива изменить на n (сделать n строк в таблице):
a.resize(n);
Затем размер каждого массива-строки необходимо изменить на m. Это можно сделать циклом:
for (int i = 0; i < n; ++i) a[i].resize(m);
Заметим, что строки могут иметь разную длину, например, можно сделать массив в форме “лесенки”, где каждая строка будет содержать на один элемент больше предыдущей:
for (int i = 0; i < n; ++i) a[i].resize(i + 1);
Но если необходимо создать прямоугольный массив, то можно сразу же при объявлении задать его размер, если воспользоваться конструктором для вектора с параметрами. Первый параметр — размер вектора, второй необязательный параметр — значение, которым будут инциализированы элементы вектора. Тогда в качестве первого параметра можно передать n, а в качестве второго параметра можно явно указать конструктор, который создает вектор из m элементов типа int : vector(m) . Итак, создать прямоугольную таблицу размером n×m можно в одну строку:
vector > a(n, vector(m));
Если вложенному вызову конструктора (для строки) передать второй параметр, то все элементы массива будут заполнены переданным значением вместо нуля. int A[n] создает в памяти одномерный массив: набор пронумерованных элементов, идущих в памяти последовательно. К каждому элементу массива можно обратиться, указав один индекс - номер этого элемента. Но можно создать и двумерный массив следующим образом: int A[n][m] . Данное объявление создает массив из n объектов, каждый из которых в свою очередь является массивом типа int [m] . Тогда A[i] , где i принимает значения от 0 до n-1 будет в свою очередь одним из n созданных обычных массивов, и обратиться к элементу с номером j в этом массиве можно через A[i][j] .
Подобные объекты (массивы массивов) также называют двумерными массивами. Двумерные массивы можно представлять в виде квадратной таблицы, в которой первый индекс элемента означает номер строки, а второй индекс – номер столбца. Например, массив A[3][4] будет состоять из 12 элементов и его можно записать в виде
A[0][0] A[0][1] A[0][2] A[0][3] A[1][0] A[1][1] A[1][2] A[1][3] A[2][0] A[2][1] A[2][2] A[2][3]
Для считывания, вывода на экран и обработки двумерных массивов необходимо использовать вложенные циклы. Первый цикл – по первому индексу (то есть по всем строкам), второй цикл – по второму индексу, то есть по всем элементам в строках (столбцам). Например, вывести на экран двумерный массив в виде таблицы, разделяя элементы в строке одним пробелом можно следующим образом:
for (int i = 0; i < n; ++i) < // Выводим на экран строку i for (int j = 0; j < m; ++j) < cout cout
А считать двумерный массив с клавиатуры можно при помощи еще более простого алгоритма (массив вводится по строкам, то есть в порядке, соответствующему первому примеру):
for (i = 0; i < n; ++i) < for (j = 0; j < m; ++j) < cin >> a[i][j]; > >
Обработка двумерного массива
Обработка двумерных массивов производится аналогичным образом. Например, если мы хотим записать в массив таблицу умножения, то есть присвоить элементу a[i][j] значение i * j , это можно сделать следующим образом при помощи вложенных циклов:
for (i = 0; i < n; ++i) < for (j = 0; j < m; ++j) < a[i][j] = i * j; >>
Рассмотрим более сложную задачу и несколько способов ее решения. Пусть дан квадратный двумерный массив размером n×n. Необходимо элементам, находящимся на главной диагонали проходящей из левого верхнего угла в правый нижний (то есть тем элементам a[i][j] , для которых i == j ) присвоить значение 1 , элементам, находящимся выше главной диагонали – значение 0, элементам, нахощящимся ниже главной диагонали – значение 2. То есть получить такой массив (пример для n == 4 ):
1 0 0 0 2 1 0 0 2 2 1 0 2 2 2 1
Рассмотрим несколько способов решения этой задачи. Элементы, которые лежат выше главной диагонали – это элементы a[i][j] , для которых i < j , а для элементов ниже главной диагонали i >j . Таким образом, мы можем сравнивать значения i и j и по ним определять значение a[i][j] . Получаем следующий алгоритм:
for (i = 0; i < n; ++i) < for (j = 0; j < n; ++j) < if (i < j) < a[i][j] = 0; >else if (i > j) < a[i][j] = 2; >else < a[i][j] = 1; >> >
Данный алгоритм плох, поскольку выполняет одну или две инструкции if для обработки каждого элемента. Если мы усложним алгоритм, то мы сможем обойтись вообще без условных инструкций.
Сначала заполним главную диагональ, для чего нам понадобится один цикл:
for (i = 0; i
Затем заполним значением 0 все элементы выше главной диагонали, для чего нам понадобится в каждой из строк с номером i присвоить значение элементам a[i][j] для j = i+1 , . n-1 . Здесь нам понадобятся вложенные циклы:
for (i = 0; i < n; ++i) < for (j = i + 1; j < n; ++j) < a[i][j] = 0; >>
Аналогично присваиваем значение 2 элементам a[i][j] для j = 0 , . i-1 :
for (i = 0; i < n; ++i) < for (j = 0; j < i; ++j) < a[i][j] = 2; >>
Можно также внешние циклы объединить в один и получить еще одно, более компактное решение:
for (i = 0; i < n; ++i) < // Заполняем строку с номером i for (j = 0; j < i; ++j) < a[i][j] = 2; // Сначала пишем 2 ниже диагонали >a[i][j] = 1; // После завершения предыдущего цикла i==j, пишем 1 for (++j; j < n; ++j) // Цикл начинаем с увеличения j на 1 < a[i][j] = 0; // Записываем 0 выше диагонали >>
Многомерные массивы
Если необходимо хранить в массиве величину, зависящую от трёх параметров-индексов, например, a[i][j][k] , то для представления такого массива нужно использовать вектор, элементами которого будут двумерные векторы, то есть
vector > > a;
Их можно рассматривать как “трёхмерные” таблицы, но проще думать о таких массивах, просто как о величине, определяемой тремя параметрами-индексами.
Их тоже можно создавать в одну строку, добавив ещё один уровень вложенности конструкторов. Например, пусть требуется создать массив размера x×y×z, то есть первый индекс будет принимать значения от 0 до x-1, второй индекс от 0 до y-1, третий индекс от 0 до z-1. Можно использовать такое объявление:
vector > > a(x, vector >(y, vector(z)));
Передача двумерных массивов в функцию
Передавать двумерные массивы в функцию всегда лучше по ссылке, т.е. добавляя знак “&” перед идентификатором параметра. Например:
void print(vector > & a)
Это обязательно делать в случае, когда функция должна модифицировать передаваемый массив, если же функция не модифицирует передаваемый массив, всё равно лучше массив передавать по ссылке, так как в этом случае не производится копирование массива (если массив передается по значению, то создается копия массива, что требует дополнительного времени).
Форматирование чисел при выводе
Допустим, мы заполним массив таблицей умножения: a[i][j] = i * j как в примере в начале раздела. Если мы теперь попробуем вывести этот массив на экран, разделяя элементы в строке одним пробелом, то из-за того, что числа имеют различную длину столбцы таблицы окажутся неровными:
0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 0 2 4 6 8 10 12 14 16 18 0 3 6 9 12 15 18 21 24 27
Для того, чтобы получить ровные столбцы необходимо, выводить числа так, чтобы одно выводимое число имело ширину, например, ровно в 3 символа, а “лишние” позиции были бы заполнены пробелами. Тогда получится следующая таблица:
0 0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9 0 2 4 6 8 10 12 14 16 18 0 3 6 9 12 15 18 21 24 27
Для того, чтобы выводимое число или строка имело ровно заданную ширину, необходимо перед выводом его на экран для потока cout вызвать метод width с параметром 3 . Данный метод устанавливает ширину поля для выводимого значения. Получим следующую программу для вывода:
for(int i = 0; i < n; ++i) < for(int j = 0; j < m; ++j) < cout.width(3); cout cout
Заметим, что мы теперь не выводим пробел после каждого числа, поскольку мы добавили этот пробел к ширине выводимого поля. Функция width действует однократно, только на следующее выводимый в поток значение, поэтому ее нужно вызывать перед каждым выводом числа на экран.
Внимание! Если выводимое число или строка имеет большую длину, чем это было установлено функцией width , то это число или строка будут выведены полностью, а не будет обрезано до указанного значения. То есть предпочтительней вывести результат некрасиво, нежели неверно.
2D-векторы в C++ — Практическое руководство 2D-векторы
Также называемые векторами векторов, 2D-векторы в C++ формируют основу для динамического создания матриц, таблиц или любых других структур. Прежде чем перейти к теме двумерных векторов в C++, рекомендуется пройти руководство по использованию одномерных векторов в C++.
Включение заголовочного файла Vector
Мы не могли бы использовать векторы в C++, если бы не заголовочные файлы, включенные в начале программы. Чтобы использовать 2D-векторы, мы включаем:
#include
Вместо того, чтобы включать множество видов стандартных библиотек шаблонов (STL) один за другим, мы можем включить их все следующим образом:
#include
Инициализация 2D-векторов в C++
Во-первых, мы изучим некоторые способы инициализации двумерного вектора. Следующий фрагмент кода объясняет инициализацию двумерного вектора, когда все элементы уже известны.
#include #include using namespace std; int main()< vector> v , , >; for(int i=0;i >
После запуска приведенного выше кода мы получаем следующий вывод:
1 0 1 0 1 1 0 1
Использование vector > символизирует, что мы работаем с вектором векторов. Каждое значение внутри первого набора фигурных скобок, например и , является независимым вектором.
Примечание. Чтобы создать 2D-векторы в C++ с другим типом данных, мы можем поместить тип данных в самые внутренние угловые скобки, например .
Поскольку мы работаем с двумерной структурой данных, нам требуется два цикла для эффективного обхода всей структуры данных. Внешний цикл перемещается по строкам, а внутренний цикл — по столбцам.
Примечание. Функция size() предоставляет количество векторов внутри 2D-вектора, а не общее количество элементов внутри каждого отдельного вектора.
Указание размера для инициализации 2D-вектора
2D-векторы могут быть больших размеров. Мы не можем ожидать, что программист будет вводить каждое отдельное значение. Следовательно, мы можем инициализировать двумерный вектор на основе количества строк и столбцов.
#include #include using namespace std; int main() < //Number of columns int num_col = 3; // Number of rows int num_row = 4; // Initializing a single row vectorrow(num_col, 0); // Initializing the 2-D vector vector v(num_row, row) ; for(int i=0;i >
0 0 0 0 0 0 0 0 0 0 0 0
В соответствии со стандартной инициализацией вектора, vector v(10, 0) , первый аргумент обозначает размер вектора, тогда как второй обозначает значение по умолчанию, которое содержится в каждой ячейке.
В приведенном выше фрагменте кода мы выполняем два шага стандартной инициализации:
- vector row(num_col, 0) — в этом операторе мы создаем одномерный вектор с именем row , длина которого определяется num_col и значения по умолчанию 0 . По сути, он формирует каждую строку нашего двумерного вектора.
- vector > v(num_row, row) — в этом операторе мы создаем наш полный двумерный вектор, определяя каждое значение двумерного вектора как row , созданный в последнем операторе.
Поняв описанную выше процедуру, мы можем улучшить нашу инициализацию 2D-векторов в C++ следующим образом:
#include #include using namespace std; int main()< //Number of columns int num_col = 3; // Number of rows int num_row = 4; // Initializing the 2-D vector vector> v(num_row, vector (num_col, 0)) ; for(int i=0;i >
Вышеупомянутый код выдаст такой же вывод, как и раньше, поскольку мы делаем то же самое, но в одной строке кода.
Если мы правильно помним, стандартная инициализация выглядит примерно так, как описано выше. Создание двумерного вектора требует, чтобы мы установили значение по умолчанию для каждого элемента как одномерного вектора.
Последний метод включает создание двумерного вектора без знания строк или столбцов. Это делается:
vector> v;
Приведенное выше объявление создает пустой контейнер, способный хранить элементы в виде векторов.
Итераторы для 2D-векторов
Вместо обхода двумерного вектора с использованием индексов в C++ предусмотрены итераторы для каждой конкретной структуры данных STL.
#include #include using namespace std; int main()< vector> v , , >; // Iterator for the 2-D vector vector>::iterator it1; // Iterator for each vector inside the 2-D vector vector::iterator it2; // Traversing a 2-D vector using iterators for(it1 = v.begin();it1 != v.end();it1++)< for(it2 = it1->begin();it2 != it1->end();it2++) cout >
1 0 1 0 1 1 0 1
Итераторы пригодятся, когда мы используем определенные операции, требующие аргумента для позиционирования. Две наиболее часто используемые функции, возвращающие значения итератора:
- v.begin() — возвращает итератор к первому вектору в двумерном векторе.
- v.end() — возвращает итератор в конец двумерного вектора.
Давайте посмотрим на некоторые операции, возможные на двумерном векторе.
Добавление элементов в двумерный вектор
Чтобы добавить элементы в конец двумерного вектора, мы используем функцию push_back() .
#include #include using namespace std; int main()< // Initializing the 2-D vector vector> v; v.push_back(); v.push_back(); v.push_back(); for(int i=0;i >
1 0 1 0 1 1 0 1
Поскольку наш контейнер представляет собой вектор векторов, имеет смысл помещать внутрь него только полные векторы. Следовательно, аргумент, передаваемый внутри функции push_back() , должен быть вектором.
Примечание. v[i] представляет собой одномерный вектор. Поэтому, если программисту необходимо добавить элементы в определенный вектор внутри двумерного вектора, он может использовать v[i].push_back(value) .
Чтобы добавить полный вектор в определенное место, мы используем функцию insert() .
#include #include using namespace std; int main()< // Initializing the 2-D vector vector> v; v.push_back(); v.push_back(); v.push_back(); // Iterator for the 2-D vector vector>::iterator it = v.begin(); // Inserting the vector = as the second vector v.insert(it + 1, ); for(int i=0;i >
1 0 1 1 2 3 0 1 1 0 1
Функция insert() требует позиционного аргумента в качестве итератора, а не целочисленного индекса. За ним следует вектор, который предполагается вставить в указанное место.
Удаление элементов из 2D-векторов в C++
В отличие от push_back() , C++ предоставляет функцию pop_back() , обязанную удалять последний элемент из заданного вектора.
В контексте этой статьи функция pop_back() будет отвечать за удаление последнего вектора из двумерного вектора.
#include #include using namespace std; int main()< // Initializing the 2-D vector vector> v ; // Adding vectors to the empty 2-D vector v.push_back(); v.push_back(); v.push_back(); // Remove the last vector from a 2-D vector v.pop_back(); for(int i=0;i >
1 0 1 0 1
В дополнение к функции pop_back() у нас есть функция erase() , с помощью которой мы можем удалять элементы из указанного индекса.
#include #include using namespace std; int main()< // Initializing the 2-D vector vector> v ; // Pushing vector inside the empty 2-D vector v.push_back(); v.push_back(); v.push_back(); // Iterator for the 2-D vector vector>::iterator it = v.begin(); // Remove the second vector from a 2-D vector v.erase(it + 1); for(int i=0;i >
1 0 1 1 0 1
Подобно функции insert() , она требует позиционного аргумента в качестве итератора. Чтобы удалить все векторы из двумерного вектора, можно использовать функцию clear() .
Приведенных выше функций может быть достаточно, чтобы освоиться при использовании двумерных векторов в C++.
Заключение
Двумерные векторы в C++ очень просты в использовании, при условии, что программист знает используемый синтаксис. Этот тип вектора пригодится, когда мы решаем задачи, связанные с матрицами, графами и другими двумерными объектами.
Мы надеемся, что этот учебник просветил читателя по теме использования 2-D векторов. Не стесняйтесь комментировать ниже любые вопросы, связанные с темой.
Все права защищены. © Linux-Console.net • 2019-2023
Как задать размер двумерного вектора?
Т.е. pole - вектор из n векторов, каждый из которых - вектор из m строк.
Аналогично надо объявлять и pole2 .
Отслеживать
ответ дан 10 сен 2020 в 17:32
219k 15 15 золотых знаков 119 119 серебряных знаков 230 230 бронзовых знаков
- c++
- строки
- vector
-
Важное на Мете
Связанные
Похожие
Подписаться на ленту
Лента вопроса
Для подписки на ленту скопируйте и вставьте эту ссылку в вашу программу для чтения RSS.
Дизайн сайта / логотип © 2024 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2024.1.8.3130
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Многомерные динамические массивы и многомерные векторы
Решил написать для себя маленькую шпаргалку по созданию двухмерного и трёхмерного динамического массива и таких же многомерных векторов (STL vector), но понял, что она будет полезна не только мне, поэтому я решил поделиться ей с вами 🙂
Двухмерный вектор
#include using std::vector; #define HEIGHT 5 #define WIDTH 3 int main() < vector> array2D; // Устанавливаем размерность (HEIGHT x WIDTH) array2D.resize(HEIGHT); for (int i = 0; i < HEIGHT; ++i) array2D[i].resize(WIDTH); // Помещаем некоторые значения array2D[1][2] = 6.0; array2D[3][1] = 5.5; return 0; >
Трёхмерный вектор
#include using std::vector; #define HEIGHT 5 #define WIDTH 3 #define DEPTH 7 int main() < vector> > array3D; // Устанавливаем размерность (HEIGHT x WIDTH) array3D.resize(HEIGHT); for (int i = 0; i < HEIGHT; ++i) < array3D[i].resize(WIDTH); for (int j = 0; j < WIDTH; ++j) array3D[i][j].resize(DEPTH); >// Помещаем некоторые значения array3D[1][2][5] = 6.0; array3D[3][1][4] = 5.5; return 0; >
Двухмерный динамический массив
#define HEIGHT 5 #define WIDTH 3 int main() < double **p2DArray; // Выделяем память p2DArray = new double*[HEIGHT]; for (int i = 0; i < HEIGHT; ++i) p2DArray[i] = new double[WIDTH]; // Помещаем некоторые значения p2DArray[0][0] = 3.6; p2DArray[1][2] = 4.0; // Освобождаем выделенную ранее память for (int i = 0; i
Трёхмерный динамический массив
#define HEIGHT 5 #define WIDTH 3 #define DEPTH 7 int main() < double ***p2DArray; // Выделяем память p2DArray = new double**[HEIGHT]; for (int i = 0; i < HEIGHT; ++i) < p2DArray[i] = new double*[WIDTH]; for (int j = 0; j < WIDTH; ++j) p2DArray[i][j] = new double[DEPTH]; >// Помещаем некоторые значения p2DArray[0][0][0] = 3.6; p2DArray[1][2][4] = 4.0; // Освобождаем выделенную ранее память for (int i = 0; i < HEIGHT; ++i) < for (int j = 0; j < WIDTH; ++j) delete [] p2DArray[i][j]; delete [] p2DArray[i]; >delete [] p2DArray; return 0; >
Важная памятка
Ну и напоследок расскажу про грабли на которые я сам однажды наступил.
Если вы помещаете в вектор или динамический массив указатели на динамически создаваемые объекты некоторого класса myClass, а потом очищаете вектор/массив, то помните, что деструктор для вашего myClass вызываться не будет, его необходимо вызывать самостоятельно, иначе получите утечку памяти (memory leak).
Проще показать на примере:
#include #include using namespace std; class myClass < public: myClass() < cout ~myClass() < cout >; int main() < vectorarray; array.push_back(new myClass()); array.push_back(new myClass()); /* Вот этот кусок кода забывать нельзя! */ for (int i = 0; i < array.size(); i++) < delete array[i]; // тут будут вызваны деструкторы для myClass >/*--------------------------------------*/ array.clear(); return 0; >
Share the post "Многомерные динамические массивы и многомерные векторы"