Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Data Structures and Algorithms in C++ 2e (На ру...docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
2.37 Mб
Скачать

238 Глава 6. Список и Iterator adTs

6.2 Списки

Используя индекс не единственные средства обращения к месту, где элемент появляется в списке. Если нам осуществили список L с (отдельно или вдвойне) связанный список, то это могло возможно быть более естественным и эффективным использовать узел вместо индекса как средство идентификации, где получить доступ и обновить список. В этой секции определите список ADT, который резюмирует связанную структуру данных списка бетона (представленный в Разделах 3.2 и 3.3) использование связанного положения ADT, который резюмирует понятие «места» в списке.

6.2.1 Основанный на узле Operations и Iterators

Позвольте L быть (отдельно или вдвойне) связанный список. Мы хотели бы определить функции для L

то взятие узлы списка как параметры и обеспечивает узлы как типы возвращения. Такие функции могли обеспечить значительные ускорения по основанным на индексе функциям, потому что нахождение индекса элемента в связанном списке требует поиска списка с приращением с его начала или конца, считая элементы, когда мы идем.

Например, мы могли бы хотеть определить гипотетическую функцию, удаляют (v), который удаляет элемент L, сохраненного в узле v списка. Используя узел в качестве параметра позволяет нам удалять элемент в O (1) время, просто идя непосредственно в место, где тот узел сохранен и затем «соединение» этот узел посредством обновления следующих и предыдущих связей его соседей. Точно так же в O (1) время, мы могли вставить новый элемент e в L с операцией, такой как вставка (v, e), который определяет узел v, перед которым должен быть вставлен узел нового элемента. В этом случае мы просто «связываемся в» новом узле.

Определение функций списка, ADT, добавляя такие основанные на узле операции поднимает проблему того, сколько информации мы должны выставлять о implementa-tion нашего списка. Конечно, желательно для нас быть в состоянии использовать или отдельно или вдвойне связанный список, не показывая эту деталь пользователю. Аналогично, мы не хотим позволять пользователю изменять внутреннюю структуру списка без нашего ведома. Такие модификации были бы возможны, однако, если бы мы обеспечили указатель на узел в нашем списке в форме, которая позволяет пользователю получать доступ к внутренним данным в том узле (таком как следующая или предыдущая область).

Чтобы резюмировать и объединить различные способы сохранить элементы в различном im-plementations списка, мы вводим тип данных, который резюмирует понятие относительного положения или место элемента в рамках списка. Такой объект можно было бы естественно назвать положением. Поскольку мы хотим этот объект не только к отдельным элементам доступа списка, но также и перемещаться, чтобы перечислить все элементы списка, мы принимаем соглашение, используемое в C ++ Стандартная Библиотека Шаблона, и называем его iterator.

6.2. Списки 239

Контейнеры и положения

Чтобы безопасно расширить набор операций для списков, мы резюмируем понятие

«положение», которое позволяет нам наслаждаться эффективностью вдвойне или отдельно связанные внедрения списка, не нарушая ориентированные на объект принципы разработки. В этой основе мы думаем о списке как о случае более общего класса объектов, названных контейнером. Контейнер - структура данных, которая хранит любую коллекцию элементов. Мы предполагаем, что элементы контейнера могут быть устроены в линейном заказе. Положение определено, чтобы быть абстрактным типом данных, который связан с особым контейнером и который поддерживает следующую функцию.

элемент (): Возвратите ссылку на элемент, сохраненный в этом положении.

C ++ способность перегрузить операторов предоставляет нам изящного альтернативного человека - ner, в котором можно выразить операцию по элементу. В частности мы перегружаем deref-erencing оператор (» * «), так, чтобы, учитывая переменную положения p, к связанному элементу можно было получить доступ *p, а не p.element (). Это может использоваться и для доступа и для изменения стоимости элемента.

Положение всегда определяется относительным способом, то есть, с точки зрения его ржания - bors. Если это не первое или последним из контейнера, положение q всегда «после» некоторого положения p и «перед» некоторым положением r (см. рисунок 6.4). Положение q, которое связано с некоторым элементом e в контейнере, не изменяется, даже если индекс e изменяется в контейнере, если мы явно не удаляем e. Если связанный узел удален, мы говорим, что q лишен законной силы. Кроме того, положение q не изменяется, даже если мы заменяем или обмениваем элемент e снабженный в q другим элементом.

Балтимор Нью-Йорк Пекин Дели

p q r s

Рисунок 6.4: контейнер списка. Положения в текущем заказе - p, q, r, и s.

Iterators

Хотя положение - полезный объект, было бы более полезно все еще быть в состоянии к

проведите через контейнер, например, продвинувшись к следующему положению в контейнере. Такой объект называют iterator. iterator - расширение положения. Это поддерживает способность получить доступ к элементу узла, но это также обеспечивает способность провести форвардов (и возможно назад) через контейнер.

Есть много путей, которыми можно определить ADT для объекта iterator. Например, учитывая iterator возражают p, мы могли определить операцию p.next (), который возвращает iterator, который относится к узлу сразу после p в контейнере. Будьте - причина C ++ способность перегрузить операторов, есть более изящный способ сделать

240

Глава 6. Список и Iterator ADTs это, перегружая оператора приращения (» ++ «). В частности операция ++ p достижения p к следующему положению контейнера. Неоднократно применяя эту операцию, мы можем ступить через все элементы контейнера. Для некоторых imple-процессов мышления контейнеров, таких как вдвойне связанный список, навигация может быть возможной и вперед и назад. Если так, мы можем также перегрузить оператора декремента (» - «), чтобы переместить iterator в предыдущее положение в контейнере.

В дополнение к навигации через контейнер нам нужен некоторый способ начальной буквы - izing iterator к первому узлу контейнера и определения, пошло ли это вне конца контейнера. Чтобы сделать это, мы предполагаем, что каждый контейнер обеспечивает две специальных ценности iterator, начните и закончите. Начало iterator относится к первому положению контейнера. Мы думаем об окончании iterator как относящийся к воображаемому положению, которое находится сразу после последнего узла контейнера. Учитывая контейнерный объект L, операция L.begin () возвращает случай начала iterator для L и операции, L.end () возвращает случай из окончания iterator. (См. рисунок 6.5.)

Балтимор Нью-Йорк Пекин Дели

L.begin () L.end ()

Рисунок 6.5: специальный iterators L.begin () и L.end () для списка L.

Чтобы перечислить все элементы данного контейнера L, мы определяем проход - ator p, чья стоимость инициализирована к L.begin (). К связанному элементу получают доступ, используя *p. Мы можем перечислить все элементы контейнера, продвинувшись p к следующему узлу, используя операцию ++ p. Мы повторяем это, пока p не равен L.end (), что означает, что мы упали с конца списка.

6.2.2 Тип данных резюме списка

Используя понятие iterator, чтобы заключить в капсулу идею «узла» в списке, мы можем

определите другой тип последовательности ADT, названный просто список ADT. В дополнение к вышеупомянутым функциям мы включаем универсальный размер функций и пустой с обычными значениями. Этот ADT поддерживает следующие функции для списка L и iterator p для этого списка.

начните (): Возвратите iterator, относящийся к первому элементу L; то же самое

как конец (), если L пуст.

конец (): Возвратите iterator, относящийся к воображаемому элементу просто

после последнего элемента L.

insertFront (e): Вставьте новый элемент e в L как первый элемент.