Составные типы данных Прикладное программирование кафедра прикладной и компьютерной оптики 2 Составные типы данных Гомогенные Назначение для хранения однотипных переменных Пример массив, строка, список, стек, очередь, набор, карта, дерево Гетерогенные для хранения неоднородных разнотипных переменных структура, класс 3 Массивы Массив - это группа последовательных ячеек памяти, имеющих один и тот же тип int data[4]; Нумерация элементов – от 0 до <размер-1> data[0]=1; data[1]=2; data[2]=3; data[3]=4; Объявление и инициализация double v[3]={0.3, 2.2, 2.}; char symbols[]={'a', 'b', 'c', 'd', 'e'}; 4 Динамическое размещение массива double *data; // указатель на массив data=new double[1000]; // указатель на 0-й элемент массива // инициализация for(int i=0; i<1000; i++) { data[i]=0.; } delete [] data; // обязательно освободить память data+1; // указатель на 1-й элемент data+100; // указатель на 100-й элемент, *(data+4); // эквивалентно data[4] См. пример программы 5 Передача массива в функцию Передача массива в функцию может осуществляться только по указателю на нулевой элемент, с дополнительной информацией о количестве элементов в массиве // указатель на массив + количество элементов в массиве double sum(double* m, int n) // или double m[] { double s=0.; for(int i=0; i<n; ++i) { s+=m[i]; } return s; } См. пример программы 6 Двумерный массив Для создания двумерного массива необходимо указать в квадратных скобках два значения – количество строк и столбцов в массиве double matrix[3][2]; // двумерный массив 2х3 double sum=0.; for(int i=0; i<3; i++) // номер строки { for(int j=0; j<2; j++) // номер столбца { sum+=matrix[i][j]; } } [0][0] [0][1] [1][0] [1][1] [2][0] [2][1] matrix[0][0]; matrix[2][1]; [0][0] [0][1] [1][0] [1][1] [2][0] [2][1] 7 Динамическое размещение двумерного массива Двумерный массив можно разместить динамически, только если представить его как одномерный вместо доступа по индексам [i][j], придется вычислять индекс одномерного массива, которому будет соответствовать необходимый элемент двумерного массива • Например, элементу [1][0] двумерного массива будет соответствовать индекс [2] одномерного массива // указатель на двумерный массив matrix[3][2] double *matrix=new double[3*2]; // matrix[i][j] matrix[i*2+j]=0.; // например matrix[1][0] matrix[1*2+0]=0.; 0 1 2 3 4 5 [0][0] [0][1] [1][0] [1][1] [2][0] [2][1] delete [] matrix; //освободить память См. пример программы 8 Контейнеры Контейнер – тип данных, предназначенный для хранения однотипных данных вектор, стек, очередь, список, ассоциативный массив, дерево Хранение элементов контейнера последовательно (непрерывным блоком) разбросаны по всей памяти Доступ к элементам контейнера по индексу только последовательным перебором 9 STL (Standard Template Library) – стандартная библиотека шаблонов Контейнеры объекты для хранения однотипных данных: • последовательные (vector, deque, list); • ассоциативные (set, multiset, map, multimap); Адапторы объекты, созданные на основе базовых контейнеров, с измененным интерфейсом (например, queue, stack) Итераторы объекты, которые используются для универсального доступа к элементам хранящимся в контейнере любого типа Алгоритмы обобщенные процедуры для обработки элементов любых контейнеров Функции-объекты объекты, у которых перегружен оператор вызова функций 10 Контейнеры: вектор, стек, очередь Вектор хранение элементов единым блоком доступ к любому элементу по индексу Стек хранение элементов единым блоком доступ только к последнему элементу, внесенному в стек Очередь хранение элементов единым блоком доступ только к первому элементу, внесенному в очередь 11 Контейнеры: список, дерево, ассоциативный массив Список элементы разбросаны по памяти • каждый элемент содержит указатель на последующий и предыдущий только перебор Дерево элементы разбросаны по памяти обхода дерева от корня • родитель содержит ссылки на дочерние элементы Ассоциативный массив элементы разбросаны по памяти обход дерева по ключу 12 Вектор Вектор - одномерный массив проиндексированных элементов • Доступ к функциям вектора производится через оператор "." • Подробнее о классах и шаблонах см. главу 4 и раздел 5.4. #include <vector> using namespace std; vector<double> x; // создание вектора x.resize(10); // изменение размера вектора x.resize(x.size()+100); // изменение размера вектора double sum=0.0; for(int i=0; i<x.size(); i++) { sum+=x[i]; //доступ по индексу } 13 Функции работы с вектором Размер (количество элементов) контейнера int n=x.size(); Изменение размера контейнера x.resize(100); Доступ к элементам контейнера x[i]=5; Добавить элемент в конец контейнера • Добавляет копию элемента x.push_back(7); Определяет пустой ли контейнер bool res=x.empty(); // эквивалентно x.size() == 0 Очистить контейнер • разрушает все элементы и освобождает контейнер x.clear(); Выделение дополнительной памяти для размещения новых эл-в x.reserve(200); Определяет кол-во элементов, для которых зарезервирована.память int n=x.capacity(); Остальные функции – см.Приложение 5 См. пример программы 14 Список Список – структура данных для организации хранения элементов с эффективной вставкой и удаления в любом месте этой структуры, не требует доступа по индексу УЗЕЛ (Node) – объект, который содержит ссылки на последующий и предыдущий элементы, а также значение данного элемента списка Простой односвязный список Простой двухсвязный список Кольцевой двухсвязный список 15 Функции списка STL – простой двухсвязный список list<int> example; example.push_back(0); // вставка в конец example.push_front(1); // вставка в начало x.insert(x.begin(), 3); // вставка в любое место списка (переразмещения данных в памяти не происходит) 16 Итераторы Итератор – это обобщённый "указатель" на элемент, хранящийся в контейнере list<double> ls; list<double>::iterator it; оператор * – разыменование и получение доступа к значению элемента (*it)=5; операторы «++» и «– –» получение указателя на следующий и предыдущий элемент it++, it-- итератор на первый элемент контейнера it=ls.begin() итератор на следующий после последнего элемент контейнера it=ls.end() 17 Использование итераторов list<double> x; list<double>::iterator it; for(it=x.begin(); it!=x.end(); it++) { sum+=(*it); //доступ к элементам по итератору } it=x.begin(); while(it!=x.end()) { sum+=*it; it++; } См. пример программы 18 Двусторонняя очередь (deque) Двусторонняя очередь – последовательность, которая: поддерживает произвольный доступ к элементам (аналогично vector) поддерживает вставку и удаление в начало последовательности Кроме того: • не имеет функций-членов capacity() и reserve() • не предоставляет никаких гарантий на допустимость итератора См. пример программы 19 Стек (stack) Стек – адаптер очереди, который организует ее работу по правилу "last in, first out" (LIFO) не предоставляет функций для получения итераторов и их перебора позволяет проверить, какой элемент находится на вершине стека (top) stack<int> s; s.push(8); // s = 8 s.push(7); // s = 7 8 s.push(4); // s = 4 7 8 cout<<s.top()<<endl; // 4 s.pop(); cout<<s.top()<<endl; // 7 s.pop(); cout<<s.top()<<endl; // 8 См. пример программы 20 Очередь (queue) Очередь – это адаптер очереди, который организует ее работу по правилу "first in, first out" (FIFO) не предоставляет функций для получения итераторов и их перебора позволяет проверить, какой элемент находится на вершине стека (front) queue<int> q; q.push(8); // q = 8 q.push(7); // q = 7 8 q.push(4); // q = 4 7 8 cout<<q.front()<<endl; // 8 q.pop(); cout<<q.front()<<endl; // 7 q.pop(); cout<<q.front()<<endl; // 4 21 Ассоциативный массив Ассоциативный массив - используется для хранения связанных пар "ключ-значение" ("key-value") массив, в котором индекс может быть не целочисленного типа организуются как сбалансированное дерево узлов pair<const key, mapped_type> Ассоциативные контейнеры STL map – ассоциативный массив, по ключу в контейнере хранится одно значение multimap – ассоциативный массив с повторяющимися ключами set – массив уникальных ключей без значений multiset – массив с повторяющимися ключами без значений 22 Ассоциативный массив map Доступ к элементам по ключу map<string, double> glass; double n=glass["K8"]; • поиск по ключу • если ключ не найден, то вставляется элемент со значением по умолчанию (0) glass["K8"]=1.5; • поиск по ключу • если ключ не найден, то вставляется элемент с заданным значением Доступ к элементам по итератору (объект pair) • первый элемент (pair::first) – ключ • второй элемент (pair::second) – значение • элементы хранятся в отсортированном виде: от меньшего ключа к большему ключу map<string, double>::iterator it; for(it=glass.begin(); it!=glass.end(); it++) { cout<<it->first<<" "<<it->second<<endl; } См. пример программы 23 Ассоциативный массив set Доступ к элементам по итератору все значения уникальны и отсортированы • от меньшего ключа к большему ключу set<string> glass; map<string>::iterator it; glass.insert("K8"); glass.insert("ТK14"); glass.insert("K8"); for(it=glass.begin(); it!=glass.end(); it++) { cout<<*it<<" "<<endl; } См. пример программы 24 Алгоритмы Алгоритмы – обобщенные процедуры для обработки элементов любых контейнеров #include <algorithm> using namespace std; list<double> x; // вычисление суммы через итераторы list<double>::iterator it; for(it=x.begin(); it!=x.end(); it++) { sum+=(*it); } // вычисление суммы через алгоритмы sum=accumulate(x.begin(), x.end(), 0.0); 25 Алгоритмы не модифицирующие контейнер Алгоритмы не модифицирующие контейнер – процедуры поиска и сравнения list<string> ls; list<string>::const_iterator it; // поиск значения "К8" в диапазоне от ls.begin() до ls.end() it=find(ls.begin(), ls.end(), "К8"); 26 Алгоритмы модифицирующие значение элементов контейнера Алгоритмы модифицирующие значение элементов контейнера, но не изменяющие порядок их следования – выполнение действий над каждым элементом контейнера, поиск и замена vector<int> v(100); // заполнение всех элементов от ls.begin() до ls.end() // значением 0 fill(v.begin(), v.end(), 0); // замена всех элементов от ls.begin() до ls.end(), // равных -1 на 1 replace(v.begin(), v.end(), -1, 1); 27 Алгоритмы модифицирующие контейнер Алгоритмы модифицирующие контейнер – функции копирования, перестановок, удаления, тасования и сортировки, разбиения и слияния последовательностей vector<int> v(100); // сортировка массива sort(v.begin(), v.end()); // перестановка элементов массива в обратном порядке reverse(v.begin(), v.end()); 28 Функции-помощники Функции-помощники (перестановки и сравнения) vector<int> v(100); vector<int>::iterator it=v.begin(); it++; swap(*v.begin(), *it); 29 Численные алгоритмы #include <numeric> accumulate - накапливает результаты выполнения операций над последовательностью • (сложение элементов) inner_product - накапливает результаты выполнения операций над двумя последовательностями • (перемножение элементов) partial_sum - позволяет получить последовательность инкрементных изменений • (a, a+b, a+b+c, a+b+c+d, ...) adjacent_difference - позволяет получить последовательность декрементных изменений • (a, b-a, c-b-a, d-c-b-a, ...) См. пример программы