Добавил:
t.me Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2 семестр / Литература / Язык программирования С++. Краткий курс. Страуструп

.pdf
Скачиваний:
9
Добавлен:
16.07.2023
Размер:
31.34 Mб
Скачать

12 Алгоритмы

• • • • •

Введение Применение итераторов Типы итераторов Итераторы потоков Предикаты Обзор алгоритмов Концепты Алгоритмы над контейнерами Параллельные алгоритмы Советы

Не преумножайте сущности

сверх необходимости.

-

Уильям Оккам

12.1.

Введение

Структура

данных,

такая

как

список

или

вектор,

сама

по

себе

не

слишком

полезна.

Чтобы

ее

использовать,

нужны

основные

операции

доступа,

такие

как

добавление

и

удаление

элементов

(предусмотренные,

например,

для

list

и

vector).

Кроме

того,

мы

редко

просто

храним

объекты

в

контейнере.

Мы

сортируем

их,

выводим,

извлекаем

подмножества

элементов,

удаляем,

ищем

объекты

и

т.д.

Поэтому

стандартная

библиотека

в

дополнение

к

наиболее

рас­

пространенным

типам

контейнеров

предоставляет

и

наиболее

распростра­

ненные

алгоритмы

для

работы

с

ними.

Например,

мы

можем

просто

и

эффек­

тивно

отсортировать

вектор элементов

En

t

ry

и

поместить

копию

каждого

уникального

элемента

вектора

в

список:

void

f(vector<Entry>&

vec,

list<Entry>&

lst)

(

11

Для

упорядочения используется

оператор<:

sort(vec.begin(),vec.end());

212

Глава

12.

Алгоритмы

11

Соседние

одинаковые

элементы

не

копируются:

unique_copy(vec.begin(),vec.end(),lst.begin());

Чтобы этот код работал, для типа Entry торы меньше (<) и равно (==). Например:

должны

быть

определены

опера­

bool

operator<(const

Entry&

{

 

 

 

11 Упорядочение

записей

 

return x.name<y.name;

х,

const

Entry по

Entry&

у)

именам:

 

//Меньше,

чем

Стандартный

алгоритм

выражается

через

(полуоткрытые)

последователь­

ности

элементов.

Последовательность

представлена

парой

итераторов,

опре­

деляющих

первый

элемент

и

следующий

за

последним

элементом:

Итераторы:

begin

{)

end()

Элементы:

В

этом

примере

функция

sort

()

сортирует

последовательность,

опреде­

ленную

парой

итераторов

vec. begin

()

и

vec.

end

(),которая

просто

содер­

жит

все

элементы

вектора.

Для

записи

(вывода)

нужно

указать

только

первый

записываемый

элемент.

Если

записывается

более

одного

элемента,

то

эле­

менты,

следующие

за

этим

начальным,

будут

перезаписаны.

Таким

образом,

чтобы

избежать

ошибок,

lst

должен

иметь

как

минимум

столько

элементов,

сколько уникальных значений в vec.

Если бы мы хотели разместить уникальные то могли бы написать

элементы

в

новом

контейнере,

list<Entry> {

f(vector<Entry>&

vec)

list<Entry> res;

 

sort(vec.begin(),vec.end()

11 Добавление к

res:

);

unique_copy(vec.begin(),vec.end(),back_inserter(res)); return res;

Вызов

back_

inserter

(

res)

создает

итератор

для

res,

который

добав­

ляет

элементы

в

конец

контейнера,

расширяя

последний

так,

чтобы

для

них

нашлось

место.

Это

избавляет

нас

от

необходимости

сначала

выделять фик­

сированное

количество

памяти,

а

затем

заполнять

его.

Таким

образом,

стан­

дартные

контейнеры

совместно

с

back_inserter()

исключают

необходи-

216

Глава

12.

Алгоритмы

быть обычным

указателем, потому что указатель

ным средством

обращения к элементу вектора.

является

довольно

разум­

Итератор:

vector:

р ZГ7ат.

н

е

n

В качестве альтернативы итератор для

затель на данные vector плюс индекс.

vector

можно

реализовать

как

ука­

Итератор:

(start

==

р,

position

==

3)

vector:

·--------------------------

 

 

е

·

Р

1

1

1

н

е

n

Такой

итератор

позволяет

выполнять

проверку

выхода

за

границы

диапазона.

Итератор

для

list

должен

быть

чем-то

более

сложным,

чем

простой

ука­

затель

на

элемент,

потому

что

элемент

списка

в

общем

случае

не

знает,

где

находится

следующий

элемент

этого

списка.

Таким

образом,

итератор

списка

может

быть

указателем

на

связь

со

следующим

элементом.

Итератор:

Р

list: Элементы:

Общими

для

всех

итераторов

являются

их

семантика

и

наименование

их

операций.

Например,

применение

оператора

++

к

любому

итератору

дает

ите­

ратор,

который

указывает

на

следующий

элемент.

Точно

так

же

оператор

*

дает

элемент,

на

который

указывает

итератор.

Фактически

любой

объект,

который

подчиняется

нескольким

простым

правилам,

подобным

этим,

явля­

ется

итератором.

Iterator -

это

концепт

(§7.2,

§12.7).

Кроме

того,

пользова­

телям

редко

нужно

знать

тип

конкретного

итератора;

каждый

контейнер

"зна­

ет"

типы

своих

итераторов

и делает

их

доступными

под

обычными

именами

i

terator

и

const

i

terator.

Например,

list<Entry>::

iterator -

это

общий

тип

итератора

для

списка

list<Entry>.

Нам

редко

приходится

беспо­

коиться

о

деталях

определения

этого

типа.

12.4.

Итераторы

потоков

ки

Итераторы представляют собой общую полезную концепцию для обработ­

последовательностей элементов в контейнерах. Однако контейнеры -

это

не

единственное

место,

где

мы

находим

последовательности

элементов.

На-

218

Глава 12.

Алгоритмы

 

 

ofstream os

{to);

 

 

ostream_iterator<string>

oo{os,"\n");

// Поток вывода в файл //Выходной итератор

to

// Ь

- вектор,

инициализированный

vector<string>

Ь {ii,eos);

sort

(b.begin ()

,b.end());

входными данными:

11 Сортировка

буфера

// //

Копирование буфера в выходной с отбрасыванием повторяюШ;1хся

поток значений:

unique_copy(b.begin(),b.end(),oo);

// Возврат состояния

ошибки

return !is.eof()

11

!os;

(§1.2.1,

§10.4):

ifstream-этo

поток

istream,

который

может

быть

присоединен

к

фай­

лу,

а

ofstream

-

это

поток

ostream,

который

может

быть

присоединен

к

файлу

(§10.7).

Второй

аргумент

ostream_iterator

используется

для

раз­

граничения выходных значений.

На самом деле эта программа

длиннее,

чем

она

должна

быть.

Мы

читаем

строки

в

vector,

затем

выполняем

sort

(),а

затем

записываем

их,

исключая

дубликаты.

Более

элегантное

решение

заключается

в

том,

чтобы

вообще

не

хранить

дубликаты.

Это

можно

сделать,

сохранив

строки

в

множество,

кото­

рое

не

сохраняет

дубликаты

и

хранит

свои

элементы

в

упорядоченном

виде

11.4).

Таким

образом,

мы

могли

бы

заменить

две

строки,

использующие

вектор,

одной,

использующей

множество,

и

заменить

unique

_сору

( )

более

простой

функцией

сору

( ) :

set<string> Ь {ii,eos); copy(b.begin(),b.end(),oo);

// Сбор

строк

из

//Вывод

строк

в

входного выходной

файла файл

 

Мы используем

имена ii,

eos

можно сократить еще сильнее:

 

int

main ()

 

 

и

оо

по

одному

разу,

так

что

программу

string

from,

to;

 

//

Получение

имен

входного

cin

>>

from

>> to;

 

и

выходного

файлов:

ifstream ofstream

is os

{from); {to);

// //

Входной

поток для

файла

Выходной

поток для

файла

from to

11

Чтение

входных

данных:

set<string>

Ь

{istream_iterator<string>{is), istream_iterator<string>{));

//

Копирование

данных

в

выходной

поток:

copy(b.begin(),b.end(),ostream_iterator<string>{os,"\n"));