Контейнеры STL представляли бы собой красивую выдумку достаточно далёкую от практического использования (как и было в первые годы существования STL), если бы не следующее обстоятельство: из-за единой общей природы всех контейнеров основные алгоритмы, представляющие интерес на практике, могут быть реализованы в обобщённом виде, применимом к любым типам контейнеров.
Алгоритмы — это самая объёмная и самая востребованная часть библиотеки. Предоставляется настолько много алгоритмов, что для детального описания их всех не хватит и объёмной книги. Ниже мы совершенно условно поделим их на группы и назовём по именам (и тоже далеко не все), и только по некоторым построим примеры использования.
for_each() Наиболее часто используемый алгоритм — это for_each(): выполнение действия для группы элементов (возможно всех) контейнера. Ниже показано несколько примеров работы алгоритма for_each() для массива и вектора, точно также этот алгоритм может использоваться с любым контейнером STL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include <iostream> #include <vector> #include <algorithm> using namespace std; inline ostream& operator <<( ostream& out, const vector< unsigned > & obj ) { cout << "< "; for( auto& p: obj ) cout << p << " "; return out << ">"; } void pow2( unsigned& i ) { i *= i; } int main( void ) { const int examples = 4; for( int i = 0; i < examples; i++ ) { unsigned ai[] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9 }, ni = sizeof( ai ) / sizeof( ai[ 0 ] ); vector< unsigned > vi( ai, ai + ni ); cout << vi; switch( i ) { case 0: for_each( vi.begin(), vi.end(), pow2 ); cout << " => " << vi << endl; break; case 1: for_each( ai, ai + ni, pow2 ); cout << " => " << vector< unsigned >( ai, ai + ni ) << endl; break; case 2: for( auto& i : ai ) pow2( i ); cout << " => " << vector< unsigned >( ai, ai + ni ) << endl; break; case 3: for_each( vi.begin() + 2, vi.end() - 2, pow2 ); cout << " => " << vi << endl; break; } } } |
Примечание: Строкой 3 показана работа новой (введена стандартом C++11) конструкции for( auto &x : …), которая имеет подобный эффект присвоения и может применяться и к массивам и к контейнерам (в функции-операторе вывода вектора в поток показан такой вариант). Эта конструкция, вообще то говоря, не является составной частью библиотеки STL или алгоритмов, но имеет тот же эффект, что и алгоритм for_each(): применить последовательно ко всем элементам коллекции.
Этот пример показывает основную логику организации всех алгоритмов: к указанному диапазону (не обязательно ко всей коллекции), ограниченному итератором начала и конца (зачастую указываемых первыми 2-мя параметрами) применяется поочерёдно функция, функтор, или предикат (функция, возвращающая логиxческий результат, позволяющий произвести отбор по какому-либо признаку).
find() Следующий по значимости алгоритм — это find(). Как интуитивно понятно из имени, это поиск некоторого элемента в коллекции. Обратите внимание, что многие контейнеры имеют метод find(), который для объекта будет вызываться как obj.find(…), в то время как алгоритм будет вызываться как find( obj:iteator, … ).
Собственно, это даже не один этот алгоритм, а целая обширная их группа, которую можно объединить по признаку того, что они отбирают элементы коллекции по какому-то признаку, условию, предикату: find(), find_if(), find_if_not(), find_first_of(), find_end(), adjacent_find(). В эту же группу, с некоторой натяжкой, можно отнести и count(), count_if(), search(), binary_search(), min(), max(), minmax_element(), min_element(), max_element(), equal() и др.
Ещё одна условная группа — это алгоритмы, некоторым образом «тасующие» коллекцию, переставляющие элементы местами, меняющие значения: fill(), replace_copy(), reverse(), rotate(), rotate_copy(), shuffle(), random_shuffle(), transform(), replace(), replace_if() и др.
Ещё группа — это алгоритмы работающие с 2-мя коллекциями, копирующие и перемещающие содержимое (причём, возможно, между коллекциями разного вида, например, vector<> в set<>): copy(), copy_if(), move(), swap_ranges(), remove_copy(), remove_copy_if(), merge(), set_intersection(), set_difference() и др.
И, наконец, совсем особая группа алгоритмов связана с разнообразными сортировками элементов внутри коллекции: sort(), stable_sort(), is_sorted(), is_sorted_until() и др. Эту интересную группу мы отложим на потом, для отдельного обстоятельного рассмотрения.
При таком обилии реализованных алгоритмов и число которых в библиотеке со временем возрастает, и при том, что большинство из них вообще толком нигде не описаны в литературе, возникает естественный вопрос: как разобраться во всём этом разнообразии? Эти сложности снимаются тем что:
Все объекты STL (контейнеры, алгоритмы) описаны в синтаксисе шаблонов (template). Поэтому их описания обязательно должны включаться в компилируемый код в составе своих заголовочных файлов (хедер-файлов).
Отправляйтесь в стандартный каталог </usr/include/c++> и найдите там хедер-файлы файлы вида stl_algo* — в них вы найдёте все прототипы функций алгоритмов. Более того, там же каждому прототипу предшествует обстоятельный комментарий, объясняющий назначение алгоритма, и объясняющий параметры вызова.
Рассмотрите примеры кода, использующие несколько основных алгоритмов STL — в сети их множество. По аналогии элементарно просто воспроизвести поведение и всех остальных алгоритмов.
Примечание: Вот из-за того, что библиотеки шаблонных классов определены в терминах template, сообщения об синтаксических ошибках компиляции становятся а). многословными, на десятки строк сообщений и б). ужасно невнятными для поиска ошибок. Это оборотная сторона медали такого мощного механизма как template, и к этому нужно быть готовым.
Как и было сказано выше, изучение примеров снимет множество вопросов, а поэтому приступим к коду … Теперь внимательно следите за руками (алгоритмов STL такое множество, что комментировать каждый – за пределами разумного объёма изложения, но все они работают подобно один другому):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | #include <iostream> #include <cstring> #include <vector> #include <map> #include <set> #include <algorithm> using namespace std; inline ostream& operator <<( ostream& out, const vector<char>& obj ) { for( auto p: obj ) cout << p; return out; } int main( void ) { char s[] = "The Life and Strange Surprizing Adventures of Robinson \ Crusoe, Of York, Mariner: Who lived Eight and Twenty \ Years, all alone in an un-inhabited Island on the \ Coast of America, near the Mouth of the Great River of \ Oroonoque; Having been cast on Shore by Shipwreck, \ wherein all the Men perished but himself. With \ An Account how he was at last as strangely deliver'd \ by Pyrates"; // название книги vector<string> vs = { "Supercalifragilisticexpialidocious", // cамое длинное слово на английском языке "Pneumonoultramicroscopicsilicovolcanoconiosis" // самый длинный термин на английском языке }; // copy & find : vector<char> v1( strlen( s ) ); copy( s, s + v1.size(), v1.begin() ); int nb = 0; for( auto is = find( v1.begin(), v1.end(), ' ' ); is != v1.end(); is = find( ++is, v1.end(), ' ' ) ) nb++; cout << "в фразе пробелов " << nb << " (" << nb + 1 << " слов)" << endl; // min & max : auto mm = minmax_element( v1.begin(), v1.end() ); cout << "диапазон символов: '" << *mm.first << "' ... '" << *mm.second << "'" << endl; // fill & reverse & rotate & shuffle : vector<char> suv( vs[ 0 ].size() ); copy( vs[ 0 ].begin(), vs[ 0 ].end(), suv.begin() ); cout << suv << endl; random_shuffle( suv.begin(), suv.end() ); cout << suv << endl; reverse( suv.begin(), suv.end() ); cout << suv << endl; rotate( suv.begin(), suv.begin() + suv.size() / 2, suv.end() ); cout << suv << endl; // set_intersection & set_difference set< char > sus, pns; for( char s: vector<char>( vs[ 0 ].begin(), vs[ 0 ].end() ) ) sus.insert( s ); for( char s: vector<char>( vs[ 1 ].begin(), vs[ 1 ].end() ) ) pns.insert( s ); vector<char> outi( 100 ), outd( 100 ); auto ret = set_intersection( sus.begin(), sus.end(), pns.begin(), pns.end(), outi.begin() ); cout << "общих литер " << ( ret - outi.begin() ) << " : " << outi << endl; ret = set_difference( sus.begin(), sus.end(), pns.begin(), pns.end(), outd.begin() ); cout << "уникальных литер " << ( ret - outd.begin() ) << " : " << outd << endl; } |
Здесь использованы контейнеры для char (как компактные, но самые неприятные в работе), над которыми выполняются разнообразные алгоритмы практически всех обозначенных групп:
if (color[g[v][i]] == 0)
{
a = make_pair(v, g[v][i]);
b = make_pair(g[v][i], v);
for (int j = 0; j < res.size(); j++)
{
if (res[j] == a || res[j] == b)
k = false;
}
if (k)
res.push_back(make_pair(v, g[v][i]));
p[g[v][i]] = v;
f = false;
dfs(g, color, p, g[v][i], res);
}
Что это было, папа? ;-)
//random_shuffle(suv.begin(), suv.end()); //The std::random_shuffle() function is deprecated, replaced by std::shuffle().
Меняем на
srand(time(NULL));
shuffle(suv.begin(), suv.end(), std::default_random_engine());