Методические указания / Специальные структуры данных
.pdfодна, а M ячеек полезных данных. При типичном значении M = 16 и хранении в ячейках целых чисел, получаем 64 "полезных" байта и 12 + (8..16) байт на ука-
затели, счетчик и описатели в куче, т.е. в 2-3 раза меньше служебной информа-
ции, чем полезной.
С одной стороны, размер блока желательно увеличивать, как для сниже-
ния накладных расходов, так и для повышения скорости доступа к элементам.
С другой – рост M, очевидно, должна сдерживать производительность опера-
ций вставки/удаления, при которых приходится осуществлять сдвиги длинных
"хвостов" при больших размерах блока. Кроме того, при больших блоках высо-
ка вероятность "хранения" в списке большого количества пустых ячеек – "но-
вые" блоки практически пусты. Для выбора эффективного размера блока необ-
ходим статистический анализ программной реализации контейнера на конкрет-
ной вычислительной системе. При этом специальным генератором создается относительно большой список, так что статистические свойства операций по-
иска, вставки и удаления приближены к свойствам среды того приложения, под которое готовится данный контейнер. Запуск генератора производится много-
кратно, каждый раз измеряется среднее время доступа, вставки и удаления,
процент фрагментации – процент пустых ячеек от общего количества. Постро-
енная функция, отражающая статистическую зависимость эффективности от размера блока М, будет иметь экстремум, которому соответствует оптимальное количество элементов в блоке.
1.2. Линейные связные индексированные списки
Узловые списки характеризуются высокой производительностью при операциях вставки и удаления – изменения затрагивают всего несколько указа-
телей, обеспечивающих "сцепление" узлов. Если рассматривать встав-
ку/удаление в аспекте модификации структуры в позиции, определяемой итера-
тором, то никакой вид линейных структур не может обеспечить среднюю про-
11 |
11 |
изводительность этих операций выше, чем узловой список. Итератор для дву-
связного списка двунаправленный, поэтому есть прямой доступ к "соседям"
слева и справа. Итератор для односвязного списка однонаправленный, поэтому доступ к "соседу" слева отсутствует. В этом случае для удаления узла следует устанавливать итератор на элемент, предшествующий удаляемому.
Однако прежде чем осуществлять непосредственно структурные измене-
ния (вставку/удаление), требуется позиционировать итератор на запрошенную позицию. Не возникает проблем с операциями над головным и хвостовым эле-
ментами, так как реализация узловых списков обычно предусматривает указа-
тели на головной и хвостовой узлы. Перемещение итератора по цепочке являет-
ся медленным, из чего следует низкая эффективность доступа к произвольному узлу. В то же время в двусвязном списке доступ к произвольному элементу принято осуществлять перемещением итератора от "головы" к "хвосту", если нужная позиция k < N/2, где N – число элементов в списке, и – от "хвоста" к "голове", если k ≥ N/2. Грубо говоря, движение происходит от ближайшей опорной точки. Если увеличить число опорных точек посредством хранения прямых указателей не только на "голову" и "хвост", но и на некоторые проме-
жуточные позиции, поиск нужной позиции будет ускорен за счет уменьшения длин проходимых цепочек (рис. 6).
0 |
1 |
2 |
3 |
4 |
... |
|
|
|
|
|
|
|
N-2 N-1 |
|||||||||
0 |
|
|
|
|
|
|
|
|
|
... |
|
|
|
|
|
|
|
|
|
|
|
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
Mid1 |
|
... |
|
|
Midk |
|
|
|
|
Tail |
||||||
|
|
Head |
|
|
|
|
Mid2 |
|
|
|
|
|
|
|||||||||
Рис. 6. Узловой список с указателями на промежуточные узлы С каждым прямым указателем ассоциируется номер позиции, и доступ к
произвольной позиции осуществляется просмотром получившейся таблицы
"указатель-позиция", выбором указателя, который имеет номер позиции, бли-
жайший к нужному, и перемещению по цепочке узлов обычным способом – к "голове" или к "хвосту" (рис. 7).
12 |
12 |
0 |
1 ... |
100 101 |
... |
500 ... |
600 ... |
700 701 ... |
999 1000 |
|||||||||||||||||||
0 |
|
|
|
|
... |
|
|
|
|
... |
|
|
... |
|
|
... |
|
|
|
|
... |
|
|
|
|
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
№Адрес
0
100
600
700
1000
Рис. 7. Пример списка с таблицей "указатель-позиция"
Таблица, пример которой приведен на рис. 7, обычно называется индекс-
ной. Поиск позиции, ближайшей к необходимой, в индексной таблице может быть обычным линейным поиском. Желательно, чтобы таблица была отсорти-
рована, тогда ее требуется просматривать не целиком, а до момента нахожде-
ния двух записей, номера позиций которых образуют интервал, содержащий
искомую позицию.
Техническая реализация индексной таблицы предполагает два варианта:
равномерное индексирование (например, когда существует прямой указатель на каждый k-ый элемент, k = 2, 3, ...) и неравномерное индексирование (диапазоны между указателями неодинаковы). Равномерная таблица ускоряет доступ по сравнению с неравномерной, если рассматривается худший случай, однако во-
прос о том, какой вариант обеспечивает лучшую эффективность в среднем,
требует дополнительного исследования.
Для того чтобы поддерживать индексирование равномерным, при каждой
операции вставки/удаления вычисляется новый интервал указателей ' = N'/S,
где S – количество ячеек таблицы, отводимых под индексы, N' – новое количе-
ство элементов списка. Так как ' – целое, перестройка равномерной таблицы при больших списках требуется не при каждой операции вставки/удаления, на-
пример, int(1000/10) = int(1001/10). Если ' = |
= N/S, где N – количество эле- |
ментов до вставки/удаления, изменения указателей не потребуется. |
|
13 |
13 |
а |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S = 4 N = 13 = 3
№Адрес
0
4
8
12
Новый узел
б |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
S = 4
N' = 14
' = 3 |
previous |
|
№Адрес
0
5
9
13
в |
|
|
|
next |
|
|
next |
|
Новый узел |
|
|
|
|
|
|
|
|
|
|
|
|
||
0 |
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
||||||||
S = 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
N' = 15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
' = 3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
next |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
№ |
Адрес |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Новый узел
г |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
S = 4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
N' = 16 |
|
|
|
|
|
previous previous |
|
|
|
|
|
|
|
|
||
' = 4 |
|
|
|
|
|
|
|
previous |
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
№Адрес
0
7
11
15
Рис. 8. Вставка узлов: а – исходный список; б, в, г – вставка в позиции 3, 2, 5
14 |
14 |
Если адрес удаляемого элемента хранится в качестве прямого указателя в таблице, потребуется вначале изменить адрес в таблице на адрес ближайшего
"соседа" слева или справа, а затем проверить, нужна ли перестройка таблицы.
В то время как адреса элементов в индексной таблице изменяются далеко не всегда, номера позиций элементов требуют изменения при каждой операции вставки/удаления. Графический пример вставки элементов в индексированный список приведен на рис. 8 (чтобы не загромождать рисунок, показаны только необходимые связи между узлами). Для вставки в позицию 3 (рис. 8, б) необхо-
димо сделать один шаг влево от соответствующего указателя по ссылке previous из узла 4. При этом ссылки не изменяются, изменились пока только номера позиций. Первый интервал стал больше двух остальных. Допустим,
вставка осуществляется снова в первый интервал, в позицию 2 (рис. 8, в): бли-
жайшая позиция 0, необходимо сделать два шага вправо от соответствующего указателя по ссылке next из узла 0. Указатели по-прежнему не изменяются.
Пусть требуется осуществить еще одну вставку в первый интервал, в позицию 5 (рис. 8, г): ближайшей является ссылка, соответствующая позиции 6, т.е. вы-
полняется один шаг влево от соответствующего указателя по ссылке previous из узла 6. Значение ' изменилось, поэтому необходимо перемещение указателей в таблице для восстановления равномерности. Первый интервал нужно умень-
шить на два узла (так как 7-5 = 2), для чего указатель позиции 7 изменяется в два шага по ссылкам previous из соответствующего узла (рис. 8, г). Второй ин-
тервал, по аналогии, уменьшается на один узел, для чего указатель позиции 11
изменяется за один шаг по ссылке previous из соответствующего узла. Переин-
дексированный список, в котором восстановлена равномерная длина интервала,
приведен на рис. 9.
Следует отметить, что указатели в связных списках – это не всегда указате-
ли в быстрой оперативной памяти, но часто – на дисковые блоки (или блоки лю-
бых блочных устройств). В рассмотренном примере потребовалось три раза осу-
ществлять косвенный доступ по ссылкам из узлов, что является в данном контек-
15 |
15 |
сте самой медленной операцией, так как все остальные – арифметические опера-
ции и операции доступа к ячейкам компактной таблицы – выполняются значи-
тельно быстрее.
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
№Адрес
0
5
10
15
Рис. 9. Переиндексированный список Неравномерное индексирование проще, в нем отсутствует этап передви-
жения ссылок из таблицы, а только увеличиваются или уменьшаются номера позиций (соответственно, при вставке и удалении), начиная от модифицируе-
мого интервала к концу таблицы. Если позиции модификации равномерно рас-
пределены по интервалам, очевидно, равномерность индексации обеспечивает-
ся автоматически. Равномерно же индексированный список "скачками" восста-
навливает одинаковую ширину интервалов, вне зависимости от распределения позиций модификации.
1.3. Линейные связные индексированные блоковые списки
Механизм индексации блоковых списков сходен с механизмом индекса-
ции узловых. Так как в блоковых списках количество элементов в блоках варь-
ируется, равномерная индексация узлов затруднена. Вместо этого можно про-
водить равномерную индексацию блоков. Блоковая структура предусматривает произвольный доступ внутри блока без перемещения по ссылкам, поэтому пока не добавляются и не удаляются новые блоки, индексная таблица не "ухудшает-
ся", хотя при этом может происходить значительное количество операций
16 |
16 |
вставки или удаления. Графический пример индексированного блокового спи-
ска с размером блока 15 приведен на рис. 10 (индексы указывают на начало со-
ответствующего блока).
0 |
10 |
5 |
15 |
10 |
1 |
0 |
№Адрес
0
15
40
Рис. 10. Индексированный блоковый список
В диапазон между двумя первыми прямыми указателями (рис. 10) можно вставить еще 15 элементов. При каждой вставке номера позиций во второй и третьей строках индексной таблицы инкрементируются. Следует отметить, что для двусвязного блокового списка в общем случае невозможно выбрать на-
правление итераций (от начала или с конца), чтобы гарантировать движение от ближайшей опорной точки. Поэтому можно условиться всегда перемещаться в одном направлении, например, от головы к хвосту.
Для индексации блоков в индексную таблицу следует добавить независи-
мый столбец, который не учитывается при доступе к элементам, но использует-
ся для переиндексации при вставке/удалении блоков, если накопилась значи-
тельная неравномерность. Индексная таблица будет содержать теперь три поля:
номер блока, номер позиции и адрес блока. Исходя из выбранного размера ин-
дексной таблицы и текущей длины списка в блоках, при каждой операции вставки/удаления блока вычисляется размер интервала индексации. В столбце индексной таблицы, отвечающей за номер блока, при необходимости происхо-
дит перераспределение номеров для восстановления равномерности, остальные два столбца в этом случае также обновляют содержимое. Перестройка таблицы предполагает полный проход по блокам для вычисления номера и адреса бло-
ков, однако она выполняется редко.
17 |
17 |
Предположим, что блоковый список содержит 10 000 элементов в 500-х
блоках, размер блока – 20. Пусть индексная таблица содержит 6 записей. Тогда диапазон индексации = 500/5 = 100 блоков. При этом можно добавлять или удалять до 5-ти блоков, т.е. 5×20 = 100 случайных элементов в один и тот же индексный диапазон, так как int(500/5) = int(501/5) = ...= int(504/5) ≠ int(505/5).
Если в списке требуется произвольный доступ, такой, что обращения к произ-
вольным ячейкам случайны и равномерно распределены, операции встав-
ки/удаления будут происходить равномерно по всем индексным диапазонам, и
полного прохода по списку не потребуется в течение длительного времени. Ин-
терес также представляют случаи, когда операции произвольного доступа про-
исходят не по равномерному, а согласно иным законам распределения. Для соз-
дания генератора случайной величины с заданным законом распределения мо-
жет использовать метод обратных функций. Он заключается в том, что для за-
данной функции распределения F(x) случайной величины находят обратную к ней функцию Q(x) такую, что Q(F(x)) ≡ x. Затем, подставляя в качестве аргу-
мента функции Q(x) значение равномерно распределенной на [0,1] случайной величины r, получают случайную величину Q(r) с требуемым законом распре-
деления F(x). Для генерации приблизительно нормально распределенной слу-
чайной величины с нулевым математическим ожиданием и единичной диспер-
сией можно вначале сгенерировать s равномерно распределенных на [0,1] слу-
чайных величин Ri, а затем вычислить величину X = (R1+R2+...+Rs-s/2)/ (s/12)1/2.
2. Цель работы
Получение навыков создания и использования блоковых, индексирован-
ных, индексированных блоковых связных линейных списков; анализ эффектив-
ности работы с перечисленными видами нестандартных структур данных.
18 |
18 |
3. Задание
Осуществить программную реализацию одного из трех типов списков
(п. 1), согласно указанному преподавателем варианту задания из таблицы при-
ложения, которая должна включать:
1.Реализацию основных функций интерфейса, перечисленных в п. 1.1. Для ин-
дексированных и индексированных блоковых списков размер индексной таблицы должен задаваться пользователем. Необходимо обеспечить долж-
ным образом обработку исключительных ситуаций (выход за границы мас-
сива, ввод некорректной информации и пр.). Список следует выводить на экран по запросу пользователя вместе со всей сопутствующей информацией
(кэш, индексная таблица, блоки).
2.Генерацию списка по запросу пользователя для указанных им входных дан-
ных. Для блоковых списков указываются количество блоков, количество элементов в блоке и некоторая информация о степени заполнения, например,
общее количество непустых элементов. Для индексированных списков ука-
зываются количество узлов, размер индексной таблицы. Для блоковых ин-
дексированных списков вводится вся информация, относящаяся к блоковым спискам плюс размер индексной таблицы. Один или несколько законов рас-
пределения, по которым должны генерироваться элементы списков, указаны
в задании для каждого варианта (см. таблицу). Следует обеспечить возмож-
ность применения всех реализованных функций к сгенерированному списку.
3.Оценку производительности, включающую расчет временных характери-
стик. В этом случае списки формируются многократно по элементам, гене-
рируемым в соответствии с заданным законом распределения. Для блоковых списков оценка производительности включает выбор оптимального значе-
ния M и оценку процента фрагментации при резком увеличении частоты операций произвольного доступа. В обоих случаях необходимо графически показать функции – зависимости среднего времени выполнения операций
19 |
19 |
произвольного доступа от количества элементов M в блоке и зависимости процента фрагментированности от упомянутой частоты. Для индексирован-
ных списков следует провести сравнительный анализ среднего времени вы-
полнения операций произвольного доступа с соответствующим временем в неиндексированном списке и оценить зависимость (показать график) сред-
него времени произвольного доступа от размера индексной таблицы. Для индексированных блоковых списков следует выбирать оптимальное значе-
ние M и оценивать зависимость среднего времени произвольного доступа от размера индексной таблицы.
Программа должна предоставлять удобный интерфейс пользователя для осуще-
ствления перечисленных выше операций.
4. Содержание отчета
1.Титульный лист.
2.Задание кафедры, номер варианта.
3.Цель работы.
4.Краткая теория.
5.Алгоритм.
6.Листинг программы с необходимыми комментариями.
7.Результаты выполнения.
8.Выводы по работе.
5.Контрольные вопросы
1.Линейные связные структуры данных.
2.Представление и интерфейс блокового списка.
3.Вставка в блоковый список, удаление из блокового списка.
4.Индексированные списки.
20 |
20 |
