Алгоритмические языки и программирование. Учебное пособие
.pdffor (unsigned int j = 0; j < 5; ++j){ |
|
|
List2.push_back (Arr2[j]); |
// List2: 15, 20, 25, 30, 35 |
|
} |
|
|
List1.reverse(); |
// reverse List1: 10 20 30 40 |
|
List1.merge (List2); |
// merge List2 into List1 |
|
List1.unique(); |
// remove duplicate 20 and 30 |
|
int size = List1.size(); |
|
|
while (!List1.empty()){ |
|
|
cout << List1.front() << ' '; |
// read item from front |
|
List1.pop_front(); |
// pop item off front |
|
} |
|
|
return 0; |
|
|
}
Метод reverse меняет порядок следования элементов на обратный, метод merge сливает вместе элементы двух списков и сохраняет их в List1, метод unique находит рядом стоящие одинаковые элемены и оставляет только один из них.
Последним последовательным контейнером является дек, который представляет собой сочетание стека и очереди и позволяет добавлять элементы как в начало, так и в конец. Нужно добавить библиотеку #include <deque>.
int main(){ deque<int> Deq;
Deq.push_back(30); |
// push items on back |
Deq.push_back(40); |
|
Deq.push_back(50); |
|
Deq.push_front(20); |
// push items on front |
Deq.push_front(10); |
|
71
Deq[2] = 33; |
// change middle item |
|
for (int j = 0; j < Deq.size(); ++j){ |
||
cout << Deq[j] << ' '; |
// display items |
|
} |
|
|
return 0; |
|
|
}
Ассоциативные контейнеры
Данные располагаются не последовательно, ключ используется для доступа к данным.
Контейнеры множество и отображение реализованы при помощи бинарных деревьев, что позволяет быстро осуществлять поиск, вставку и удаление элементов. Множество хранит набор элементов, содержащих ключи – атрибуты, используемые для упорядочения данных. Отображение хранит пары объектов: ключевой и целевой объекты. Этот контейнер часто использует метод индексирования аналогично обычному массиву, но в качестве индекса используется ключ.
Контейнеры множество и отображение сопоставляют один ключ одному значению. Контейнеры мультимножество и мультиотображение позволяют хранить несколько ключей для одного значения.
Для работы со множеством и мультимножеством нужно подключать библиотеку #include <set>, для работы с отображением и мультиотображением – #include <map>. Для работы со строками используется библиотека #include <string>.
int main(){
string names[] = {"Juanita", "Robert", // array of string objects "Mary", "Amanda", "Marie"};
// initialize set to array
set<string, less<string>> nameSet(names, names+5);
72
nameSet.insert("Yvette"); |
// insert more names |
nameSet.insert("Larry"); |
|
nameSet.insert("Robert"); |
// no effect; already in set |
nameSet.insert("Barry"); |
|
nameSet.erase("Mary"); |
// erase a name |
// display size of set |
|
cout << "\nSize=" << nameSet.size() << endl; // iterator to set
set<string, less<string>> :: iterator
iter = nameSet.begin(); // display members of set while (iter != nameSet.end()){
cout << *iter++ << '\n';
}
// get name from user
cout << "\nEnter name to search for: "; string searchName;
cin >> searchName;
// find matching name in set set<string, less<string>> :: iterator
iter1 = nameSet.find(searchName); if (iter1 == nameSet.end()){
cout << "The name " << searchName << " is NOT in the set.";
}
else{
cout << "The name " << *iter1 << " IS in the set.";
}
return 0;
}
Для контейнера set существуют методы добавления и удаления элементов, а также поиска элемента. Метод find возвращает итератор на найденный элемент или на конец контейнера, если элемент не найден.
Для работы с ассоциативными контейнерами очень важны методы работы с диапазонами.
73
int main(){
set<string, less<string>> organic; // set of string objects organic.insert("Curine"); // insert organic compounds organic.insert("Xanthine");
organic.insert("Curarine");
organic.insert("Melamine");
organic.insert("Cyanimide");
organic.insert("Phenol");
organic.insert("Aphrodine");
organic.insert("Imidazole");
organic.insert("Cinchonine");
organic.insert("Palmitamide");
organic.insert("Cyanimide");
set<string, less<string>> :: iterator
iter = organic.begin(); // display set while (iter != organic.end()){
cout << *iter++ << '\n';
}
string lower, upper; // display entries in range cout << "\nEnter range (example C Czz): ";
cin >> lower >> upper;
iter = organic.lower_bound(lower);
while (iter != organic.upper_bound(upper)){ cout << *iter++ << '\n';
}
return 0;
}
Метод lower_bound берет в качестве аргумента значение того же типа, что и ключ. Он возвращает итератор на элемент, ключ которого не меньше, чем значение аргумента. Метод upper_bound возвращает итератор на элемент, ключ когорого больше, чем аргумент.
74
Для работы с отображением нужно подключить библиотеку #include <map>.
int main(){
string states[] = { "Wyoming", "Colorado", "Nevada", "Montana", "Arizona", "Idaho"}; unsigned int pops[] = { 470, 2890, 800, 787, 2718, 944 };
map<string, int, less<string>> mapStates; string name;
unsigned int pop;
unsigned int Size = sizeof(states)/sizeof(states[0]); for (unsigned int j = 0; j < Size; ++j){
name = states[j]; |
|
//get data from arrays |
pop = pops[j]; |
|
|
mapStates[name] = pop; |
//put it in map |
|
} |
|
|
cout << "Enter state: "; |
//get state from user |
|
cin >> name; |
|
|
pop = mapStates[name]; |
|
//find population |
cout << "Population: " << pop << ",000\n"; |
||
cout << endl; |
//display entire map |
|
map<string, int, less<string>> :: iterator iter;
for (iter = mapStates.begin(); iter != mapStates.end(); ++iter){ cout << (*iter).first << ' ' << (*iter).second << ",000\n";
}
return 0;
}
В примере для описания отображения используется три шаблонных аргумента. Первый задает тип ключа (название штата), второй – значение (население), третьим аргументом задаем порядок. Мы хотим, чтобы названия штатов выводились в алфавитном порядке, поэтому третьим аргументом передаем less<string>. Для задания элементов отображения и поиска в нем используется переопределенный оператор индексации.
75
Адаптеры
Для создания из базовых контейнеров специализированных применяются конструкции, называемые адаптерами контейнеров. Они предоставляют более простой интерфейс. В библиотеке STL представлено три специализированных контейнера: стек, очередь и очередь с приоритетом. В стек данные добавляются
ичитаются с одного конца, в очередь добавляются в конец, читаются с начала.
Вочередь с приоритетом добавляются с начала, читаются в строгом соответствии с величиной хранящегося значения. Приоритет имеют данные с наибольшими значениями. Таким образом, очередь с приоритетом автоматически сортирует данные.
Для реализации адаптеров могут использоваться различные последовательные контейнеры, хотя дек используется более часто. Для использования нужно подключать соответствующую библиотеку.
stack<deque<int>> Stack;
Алгоритмы
Алгоритм – это функция, которая производит некоторые действия над элементами контейнера. Алгоритмы не являются методами класса и даже не являются дружественными функциями. Алгоритмы – независимые шаблоны функций. Их можно использовать также с массивами и вашими собственными контейнерами (предполагается, что в класс включены некоторые базовые функции). Ниже представлены некоторые типичные алгоритмы.
Алгоритм |
Назначение |
|
|
find |
Возвращает первый элемент с указанным |
|
значением |
|
|
count |
Считает количество элементов с указанным |
|
значением |
|
|
equal |
Сравнивает содержимое двух контейнеров |
|
|
76
Алгоритм |
Назначение |
|
|
|
|
|
|
search |
Ищет последовательность |
значений |
в |
|
контейнере |
|
|
|
|
|
|
copy |
Копирует последовательность |
значений |
из |
|
одного контейнера в другой |
|
|
|
|
|
|
swap |
Обменивает значения двух контейнеров |
|
|
|
|
|
|
iter_swap |
Обменивает последовательности значений |
|
|
|
|
|
|
fill |
Копирует значение во множество ячеек |
|
|
|
|
|
|
sort |
Сортирует значения в указанном порядке |
|
|
|
|
|
|
merge |
Сливает две последовательности значений |
|
|
|
|
||
accumulate |
Возвращает сумму элементов в заданном |
||
|
диапазоне |
|
|
|
|
||
for_each |
Выполняет заданную функцию для каждого |
||
|
элемента контейнера |
|
|
|
|
|
|
Для работы с алгоритмами нужно использовать библиотеку #include <algorithm>. Алгоритм find принимает в качестве параметров диапазон поиска и значение.
int main(){
int arr[] = { 11, 22, 33, 44, 55, 66, 77, 88 };
int* ptr = find(arr, arr+8, 33); //find first 33
cout << "First object with value 33 found at offset " << (ptr-arr) << endl; return 0;
}
Пример подсчета количества встреч заданного элемента в контейнере: int main(){
int arr[] = { 33, 22, 33, 44, 33, 55, 66, 77 };
int n = count(arr, arr+8, 33); //count number of 33's cout << "There are " << n << " 33's in arr." << endl; return 0;
}
77
Пример сортировки контейнера: int main(){
int arr[] = {45, 2, 22, -17, 0, -30, 25, 55}; unsigned int Size = sizeof(arr) / sizeof(arr[0]);
sort (arr, arr + Size); |
// sort the numbers |
|
for (unsigned int j = 0; j < Size; ++j){ |
// display sorted array |
|
cout << arr[j] << ' ';
}
return 0;
}
Пример слияния двух контейнеров в один: int main(){
int src1[] = { 2, 3, 4, 6, 8 }; int src2[] = { 1, 3, 5 };
int dest[8];
//merge src1 and src2 into dest
merge (src1, src1 + 5, src2, src2 + 3, dest); for (int j = 0; j < 8; ++j){ // display dest
cout << dest[j] << ' ';
}
return 0;
}
Некоторые алгоритмы имеют версию с окончанием _if. Они принимают дополнительный параметр, который является функцией или функтором. Он возвращает логические значения и называется предикатом.
Пример предиката: //--------------------------------------------------------------
bool isDon (const string &name){ // predicate return name == "Don";
}
//--------------------------------------------------------------
78
Пример использования предиката: int main(){
string names[] = { "George", "Estelle", "Don", "Mike", "Bob" }; string* ptr = find_if (names, names+5, isDon);
if (ptr == names+5){
cout << "Don is not on the list.\n";
}
else{
cout << "Don is element "
<<(ptr-names)
<<" on the list.\n";
}
return 0;
}
//--------------------------------------------------------------------
void inches_to_cm(double in){ // convert and display as centimeters cout << (in * 2.54) << ' ';
}
//--------------------------------------------------------------------
int main(){
double inches[] = { 3.5, 6.2, 1.0, 12.75, 4.33 }; // array of inches values // output as centimeters
for_each(inches, inches+5, inches_to_cm); return 0;
}
//-------------------------------------------------------------------
double inches_to_cm(double in){ // convert inches to centimeters return (in * 2.54); // return result
}
//-------------------------------------------------------------------
int main(){
double inches[] = { 3.5, 6.2, 1.0, 12.75, 4.33 }; double centi[5];
79
// transform into array centi[]
transform(inches, inches+5, centi, inches_to_cm);
for (int j = 0; j < 5; ++j){ // display array centi[] cout << centi[j] << ' ';
}
return 0;
}
Итераторы
Итераторы – это сущности, напоминающие указатели. Они используются для получения доступа к элементам контейнера и для последовательного продвижения по контейнеру от элемента к элементу (этот процесс называется итерацией). К итератору можно применить перегруженный оператор инкремента ++, после чего итератор сдвигается на следующий элемент, и оператор разыменования *, после чего можно получить доступ к значению элемента.
Существует три основных типа итератора: прямые, двунаправленные, со случайным доступом. Прямой итератор может проходить по контейнеру только в прямом направлении. Двунаправленный может двигаться в прямом и обратном направлении, для чего используется оператор декремента “–“. Итератор со случайным доступом может двигаться в любом направлении и перескакивать на любое количество элементов.
Существует также два специализированных вида итератора. Это входной итератор, который может указывать на устройство ввода (cin или входной файл) и последовательно считывает элементы, и выходной, который указывает на устройство вывода и выводит элементы.
Пример вывода элементов списка: int main(){
int arr[] = { 2, 4, 6, 8 };
80
