C++. STL. Алгоритмы, не изменяющие значения и порядок следования элементов

Часть 1. Алгоритмы, не изменяющие значения и порядок следования элементов (немодифицирующие алгоритмы). Поиск. Примеры


Содержание


Поиск на других ресурсах:

1. Алгоритм find. Определение, есть ли элемент в последовательности

Алгоритм find находит позицию первого вхождения элемента с заданным значением в заданном диапазоне. Распространенная реализация алгоритма имеет такое объявление

template <class InputIterator, class Type>
  InputIterator find(
  InputIterator first,
  InputIterator last,
  const Type& value);

здесь

  • first, last – входные итераторы, задающие соответственно начало и конец рассматриваемого диапазона;
  • value – искомое значение.

Пример.

#include <iostream>
#include <vector>
#include <queue>
#include <list>
#include <algorithm>
using namespace std;

void main()
{
  // Алгоритм find - поиск значения в контейнере
  // 1. Поиск строки в массиве vector
  vector<string> V;
  V.push_back("abc");
  V.push_back("abcd");
  V.push_back("def");
  V.push_back("jklmn");
  V.push_back("jprst");

  vector<string>::const_iterator p = find(V.begin(), V.end(), "def");

  if (p == V.end())
    cout << "\"def\" is not found" << endl;
  else
    cout << "\"def\" is found" << endl;

  // 2. Поиск числа в списке
  list<double> L{ 2.8, 3.3, 4.5, 1.7, 2.2 };
  list<double>::iterator pL;
  pL = find(L.begin(), L.end(), 4.5);

  if (pL != L.end())
    cout << "The number is found." << endl;
  else
    cout << "The number is not found" << endl;
}

 

2. Алгоритм find_if. Определить первое значение, соответствующее заданному условию

Алгоритм find_if находит позицию первого вхождения элемента, соответствующего заданному условию в заданном диапазоне.

Распространенная реализация алгоритма имеет следующую общую форму

template <class InputIterator, class UnaryPredicate>
InputIterator find_if(
  InputIterator first,
  InputIterator last,
  UnaryPredicate pred);

где

  • first, last – входные итераторы, задающие соответственно начало и конец рассматриваемого диапазона;
  • pred – унарный предикат (объект функции) или лямбда-выражение, задающее условие, которому должен соответствовать искомый элемент.

Пример 1.

Находится первый элемент, имеющий значение больше 10.

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// Объявить унарный предикат
bool More_10(int value)
{
  return value > 10;
}

void main()
{
  // Алгоритм find_if. Определить первое число, которое больше 10
  vector<int> V = { 4, 8, 9, 2, 10, 15 };
  vector<int>::iterator p1;

  p1 = find_if(V.begin(), V.end(), More_10);

  if (p1 != V.end())
    cout << "*p1 = " << *p1 << endl;
  else
    cout << "Not found" << endl;
}

Результат

*p1 = 15

Пример 2. Используется лямбда-выражение для поиска первого числа, которое больше 10.

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;

void main()
{
  // Алгоритм find_if. Определить первое число, которое больше чем 10
  // на основе лямбда-выражения
  vector<int> V = { 4, 8, 9, 2, 10, 15 };
  vector<int>::iterator p1;

  // Здесь используется лямбда-выражение: [](int t) { return t > 10; }
  p1 = find_if(V.begin(), V.end(), [](int t) { return t > 10; });

  if (p1 != V.end())
    cout << "*p1 = " << *p1 << endl;
  else
    cout << "Not found" << endl;
}

 

3. Алгоритм find_first_of. Поиск первого элемента подпоследовательности, совпадающего с любым другим элементом последовательности

Алгоритм find_first_of производит поиск любого из нескольких значений в целевом диапазоне. Можно также задавать критерий поиска с помощью определенного предиката или лямбда-выражения.

template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 find_first_of(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2);

template <class ForwardIterator1, class ForwardIterator2, class BinaryPredicate>
ForwardIterator1 find_first_of(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2,
  BinaryPredicate pred);

здесь

  • first1, last1 – прямые итераторы, указывающие диапазон для поиска;
  • first2, last2 – прямые итераторы, указывающие диапазон для сравнения;
  • pred – бинарный предикат или лямбда-выражение, задающее условие, при котором два элемента считаются эквивалентными друг другу. Предикат возвращает true в случае соответствия.

Пример.

#include <iostream>
#include <queue>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
using namespace std;

// Предикат, определяющий условие выполнения алгоритма find_first_of
bool More_Than(int a, int b)
{
  // поиск совпадения до тех пор, пока
  // первое значение не станет больше второго,
  // то есть элемент первой последовательности не станет
  // больше элемента второй последовательности
  return a > b;
}

int main()
{
  // Алгоритм find_first_of - поиск первого элемента подпоследовательности,
  // который совпадает с любым другим элементом последовательности.

  // 1. Использование алгоритма без предиката
  // 1.1. Объявить два вектора и вывести их
  vector<int> V1 = { 2, 1, 3, 2, 4, 5, 2, 1, 3, 0 };
  vector<int> V2 = { 4, 5 };

  vector<int>::iterator it = V1.begin();
  cout << "V1 => ";
  while (it != V1.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  it = V2.begin();
  cout << "V1 => ";
  while (it != V2.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  // 1.2. Объявить итератор на результирующий элемент
  vector<int>::iterator resItem;

  // 1.3. Вызов алгоритма без предиката или лямбда-выражения
  resItem = find_first_of(V1.begin(), V1.end(), V2.begin(), V2.end());

  // 1.4. Вывести значение на которое указывает resItem
  if (resItem == V1.end())
    cout << "resItem -> V1.end()" << endl;
  else
    cout << "resItem -> " << *resItem << endl;

  // 1.5. Вывести последовательность начиная с resItem
  vector<int>::iterator it2 = resItem;
  cout << "resItem: ";
  while (it2 != V1.end())
  {
    cout << *it2 << " ";
    it2++;
  }
  cout << endl;

  // 2. Использовать алгоритм с предикатом More_Than
  // 2.1. Объявить новые два вектора
  vector<int> V3 = { 1, 3, 5, 7, 9, 10 };
  vector<int> V4 = { 2, 4, 6 };

  // 2.2. Вывести эти векторы
  it = V3.begin();
  cout << "V3 => ";
  while (it != V3.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  it = V4.begin();
  cout << "V4 => ";
  while (it != V4.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  // 2.3. Объявить новый итератор-результат
  vector<int>::iterator resItem2;

  // 2.4. Вызвать алгоритм find_first_of с предикатом
  resItem2 = find_first_of(V3.begin(), V3.end(), V4.begin(), V4.end(), More_Than);

  // 2.5. Вывести значение на которое указывает resItem2
  if (resItem2 == V3.end())
    cout << "resItem2 -> V3.end()" << endl;
  else
    cout << "resItem2 -> " << *resItem2 << endl;

  // 2.6. Вывести последовательность начиная с resItem2
  vector<int>::iterator it3 = resItem2;
  cout << "resItem2: ";
  while (it3 != V3.end())
  {
    cout << *it3 << " ";
    it3++;
  }
}

Результат

V1 => 2 1 3   2 4 5 2 1   3 0
V1 => 4 5
resItem -> 4
resItem: 4 5 2   1 3 0
V3 => 1 3 5   7 9 10
V4 => 2 4 6
resItem2 -> 3
resItem2: 3 5 7   9 10

 

4. Алгоритм find_end. Поиск последнего вхождения подпоследовательности, содержащейся в заданном диапазоне

Алгоритм find_end ищет в диапазоне последнюю подпоследовательность, совпадающую с заданной последовательностью.

Алгоритм имеет следующие перегруженные реализации

template<class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 find_end(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2);

template<class ForwardIterator1, class ForwardIterator2, class Pred>
ForwardIterator1 find_end(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2,
  Pred pred);

здесь

  • first1, last1 – прямые итераторы, указывающие диапазон для поиска;
  • first2, last2 – прямые итераторы, указывающие диапазон для сравнения;
  • pred – бинарный предикат или лямбда-выражение, задающее условие, при котором два элемента считаются эквивалентными друг другу. Предикат возвращает true в случае соответствия.

Пример.

#include <iostream>
#include <queue>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
using namespace std;

int main()
{
  // Алгоритм find_end - поиск последнего вхождения подпоследовательности,
  // которая содержится в заданном диапазоне.

  // 1. Использование алгоритма без предиката - поиск последовательности { 2, 1, 3 }
  // 1.1. Объявить вектор из 10 элементов и вывести его
  vector<int> V = { 2, 1, 3, 2, 4, 5, 2, 1, 3, 0 };
  vector<int>::iterator it = V.begin();
  cout << "V => ";
  while (it != V.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  // 1.2. Создать итераторы на начало и конец подпоследовательности: { 2, 1, 3 }
  vector<int>::iterator itBegin = V.begin();
  vector<int>::iterator itEnd = itBegin;
  itEnd++;
  itEnd++; // itEnd -> V[2] = 3

  // 1.3. Вызвать алгоритм find_end – получить итератор на начало подпоследовательности
  vector<int>::iterator itRes = find_end(V.begin(), V.end(), itBegin, itEnd);

  // 1.4. Вывести последовательность, которая следует перед itRes
  it = V.begin();
  cout << "Before itRes: ";
  while (it != itRes)
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  // 1.5. Вывести значение, на которое указывает itRes
  if (itRes == V.end())
    cout << "Subsequence not found." << endl;
  else
    cout << "itRes -> " << *itRes << endl; // 2

  // 2. Использование алгоритма с лямбда-выражением - поиск последовательности { 2, 1 }
  // 2.1. Объявить результирующий итератор
  vector<int>::iterator itRes2;

  // 2.2. Настроить итераторы itBegin, itEnd
  itBegin = V.begin();
  itEnd = itBegin;
  itEnd++;   // itBegin..itEnd => { 2, 1 }

  // 2.3. Вызвать алгоритм find_end
  itRes2 = find_end(
    V.begin(),
    V.end(),
    itBegin,
    itEnd,
    [](int a, int b)   // лямбда-выражение, здесь можно задать другое условие
    {
      return a == b;
    }
  );

  // 2.4. Вывести вектор к итератору itRes2
  it = V.begin();
  cout << "Before itRes2: ";
  while (it != itRes2)
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;

  // 2.5. Вывести вектор начиная с itRes2
  it = itRes2;
  cout << "itRes2 -> ";
  while (it != V.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;
}

Результат

V => 2 1 3   2 4 5 2 1   3 0
Before itRes: 2 1 3   2 4 5
itRes -> 2
Before itRes2: 2 1 3   2 4 5
itRes2 -> 2 1 3 0

 

5. Алгоритм adjanced_find. Поиск пары соседних элементов, совпадающих между собой

Общая форма перегруженных реализаций алгоритма

template <class ForwardIterator>
ForwardIterator adjacent_find(
  ForwardIterator first,
  ForwardIterator last);

template <class ForwardIterator , class BinaryPredicate>
ForwardIterator adjacent_find(
  ForwardIterator first,
  ForwardIterator last,
  BinaryPredicate pred);

где

  • first – переадресация итератора в позиции первого элемента в диапазоне поиска;
  • last – переадресация итератора в позиции за последним элементом в диапазоне поиска;
  • pred – бинарный предикат, задающий условие которому должны соответствовать значения соседних элементов в диапазоне [first; last-1].

Пример.

#include <iostream>
#include <vector>
#include <list>
#include <functional>
#include <algorithm>
using namespace std;

bool Is_Less(int a, int b)
{
  // рассматривается пара, в которой
  // предыдущий элемент больше чем следующий на 1
  return a == b + 1;
}

int main()
{
  // Алгоритм adjacent_find - поиск пары соседних элементов, которые совпадают между собой

  // 1. Использование adjanced_find без предиката
  // 1.1 Создать последовательность чисел и вывести ее на консоль
  vector<int> V1 = { 6, 8, 7, 1, -1, 3, 3, 4, 5, 5, 8, 1 };
  cout << "V1 => ";
  for (int i : V1)
    cout << i << " ";
  cout << endl;

  // 1.2. Объявить итератор на пару, которая совпадает
  vector<int>::iterator it1;

  // 1.3. Вызвать алгоритм adjanced_find
  it1 = adjacent_find(V1.begin(), V1.end());

  // 1.4. Вывести результат
  if (it1 != V1.end())
    cout << "*it1 = " << *it1 << endl; // *it1 = 3
  else
    cout << "it1->V1.end()" << endl;

  // 2. Использование adjacent_find с предикатом Is_Less
  // 2.1. Объявить итератор на совпадающую пару
  vector<int>::iterator it2;

  // 2.2. Вызвать алгоритм
  it2 = adjacent_find(V1.begin(), V1.end(), Is_Less);

  // 2.3. Вывести результат
  if (it2 != V1.end())
    cout << "*it2 = " << *it2 << endl;
  else
    cout << "it2->V1.end()" << endl;

  // 3. Использование adjanent_find с лямбда-выражением
  // 3.1. Объявить итератор позиции
  vector<int>::iterator it3;

  // 3.2. Вызвать алгоритм с указанием лямбда-выражения
  it3 = adjacent_find(
    V1.begin(),
    V1.end(),
    [](int a, int b) {
      // задать условие: a и b - непарные числа
      return (a % 2 == 1) && (b % 2 == 1);
    }
  );

  // 3.3. Обработать результат
  if (it3 != V1.end())
    cout << "*it3 = " << *it3 << endl; // 7
  else
    cout << "it3->V1.end()" << endl;
}

Результат

V1 => 6 8 7 1   -1 3 3 4 5   5 8 1
*it1 = 3
*it2 = 8
*it3 = 7

 

6. Алгоритм search. Поиск подпоследовательности в последовательности

С помощью метода search выполняется поиск подпоследовательности в заданной последовательности. Наиболее распространенные реализации алгоритма имеют вид

template <class ForwardIterator1, class ForwardIterator2>
ForwardIterator1 search(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2);

template <class ForwardIterator1, class ForwardIterator2, class BinaryPredicate>
ForwardIterator1 search(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  ForwardIterator2 first2,
  ForwardIterator2 last2
  BinaryPredicate pred);

здесь

  • first1, last1 – прямые итераторы, задающие диапазон поиска;
  • first2, last2 – прямые итераторы, задающие диапазон для сравнения;
  • pred – бинарный предикат (функция), определяющий условие, определяющее равенство (эквивалентность) двух элементов между собой.

Пример.

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

// Предикат, возвращающий равенство двух элементов
bool Is_Equal(int a, int b)
{
  // критерий равности элементов,
  // элемент другой последовательности на 1 больше за эл-т первой последовательности
  return a == b + 1;
}

int main()
{
  // Алгоритм search - поиск подпоследовательности в последовательности
  // 1. Создать список чисел
  list<int> L1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  list<int> L2 = { 2, 3, 4 };

  // 2. Вызвать поиск на основе использования предиката Is_Equal
  // 2.1. Получить итератор на результирующий контейнер
  list<int>::iterator it;
  it = search(L1.begin(), L1.end(), L2.begin(), L2.end(), Is_Equal);

  // 2.2. Обработать результат
  if (it == L1.end())
    cout << "There is no match.";
  else
    cout << *it << endl; // 3

  // 3. Вызвать поиск без использования предиката
  // 3.1. Получить итератор на результирующий контейнер
  it = search(L1.begin(), L1.end(), L2.begin(), L2.end());

  // 3.2. Обработать результат
  if (it == L1.end())
    cout << "There is no match.";
  else
    cout << *it << endl; // 2

}

Результат

3
2

 

7. Алгоритм search_n. Поиск подпоследовательности в последовательности с заданием количества элементов n

Алгоритм search_n производит поиск первой подпоследовательности в диапазоне заданного количества элементов, имеющих определенное значение. Соответствие этого значения можно задавать бинарным предикатом.

Наиболее распространенные реализации алгоритма следующие

template <class ForwardIterator1, class Diff2, class Type>
ForwardIterator1 search_n(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  Diff2 count,
  const Type& value);

template <class ForwardIterator1, class Diff2, class Type, class BinaryPredicate>
ForwardIterator1 search_n(
  ForwardIterator1 first1,
  ForwardIterator1 last1,
  Diff2 count,
  const Type& value,
  BinaryPredicate pred);

где

  • first1, last1 – прямые итераторы, задающие диапазон для поиска;
  • count – размер искомой последовательности;
  • value – значение элементов искомой последовательности.

Пример.

#include <iostream>
#include <algorithm>
#include <list>
using namespace std;

// Предикат, определящий равенство двух элементов
bool Is_Equal(int a, int b)
{
  // критерий равенства элементов,
  // элемент второй последовательности на 1 больше чем элемент первой последовательности
  return a == b + 1;
}

int main()
{
  // Алгоритм search_n - поиск подпоследовательности в последовательности
  // 1. Создать список чисел
  list<int> L1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
  list<int> L2 = { 2, 3, 4 };

  // 2. Вызвать поиск на основе использования предиката Is_Equal
  // 2.1. Объявить итератор на результирующий контейнер
  list<int>::iterator it;

  // 2.2. Определить, встречается ли число 5 тольки 1 раз в последовательности L1: { 5 }
  it = search_n(L1.begin(), L1.end(), 1, 4, Is_Equal); // 4+1 = 5

  // 2.3. Обработать результат
  if (it == L1.end())
    cout << "There is no match { 5 }.";
  else
    cout << *it << endl; // 5

  // 2.4. Определить, встречается ли число 3 подряд 2 раза: { 3, 3 }
  it = search_n(L1.begin(), L1.end(), 2, 2, Is_Equal);

  // 2.5. Обработать результат
  if (it == L1.end())
    cout << "There is no match { 3, 3 }." << endl;
  else
    cout << *it << endl;

  // 3. Вызвать поиск без использования предиката
  // 3.1. Определить, встречается ли число 7 тольки 1 раз
  it = search_n(L1.begin(), L1.end(), 1, 7);

  // 3.2. Обработать результат
  if (it == L1.end())
    cout << "There is no match {7}. ";
  else
    cout << *it << endl; // 7
}

Результат

5
There is no match { 3, 3 }.
7

 


Связанные темы