6946733fb0b02652b5245f2344a93fab.ppt
- Количество слайдов: 44
Нижегородский государственный университет им. Н. И. Лобачевского Факультет Вычислительной математики и кибернетики Деревья поиска: B-деревья Герасимов Сергей, Сморякова Валентина, Кулакович Ульяна
Содержание B-дерево q Применение и разновидности B-деревьев q Программная реализация q Сравнение B-дерева, B+-дерева, АВЛ-дерева и бинарного дерева q Н. Новгород, 2011 г. Деревья поиска: бинарное дерево поиска, B-деревья, красно-чёрные деревья. 2
B-дерево
Структуры данных во вторичной памяти Накопитель на жёстких магнитных дисках — устройство хранения информации, основанное на принципе магнитной записи. Является основным накопителем данных в большинстве компьютеров. Головка Дорожка Шпиндель чтения/записи На рис. 1 показан дисковый накопитель, состоящий из нескольких дисков, вращающихся с постоянной скоростью на общем шпинделе. Поверхность каждого диска покрыта магнитным материалом. Каждый диск читается и записывается при помощи магнитной головки, расположенной на специальном рычаге. Все рычаги с головками собраны в единый пакет, который позволяет перемещать головки вдоль радиуса по направлению к шпинделю или от него к краю дисков. Диск Рычаги Рис. 1 Устройство дискового накопителя Н. Новгород, 2011 г. 4
Структуры данных во вторичной памяти Дорожки, образующие цилиндр Дорожкой называется поверхность, проходящая под головками чтения/записи при вращении диска в фиксированном положении рычагов. Обращение к набору дорожек (цилиндр, рис. 2) выполняется одновременно, поскольку головки оказываются выровнены по вертикали. Рис. 2 Дорожки, образующие цилиндр Н. Новгород, 2011 г. 5
Структуры данных во вторичной памяти На данный момент типичная скорость вращения дисков составляет 4800 -15000 оборотов в минуту (rpm). Хотя такая скорость может показаться очень большой, один оборот требует от 2 до 6, 25 мс , что на 5 порядков превышает время обращения к оперативной памяти (которое составляет примерно 1 -10 нс). Другими словами, пока мы ждем оборота диска, чтобы считать необходимые нам данные, из оперативной памяти мы могли бы получить эти данные почти 100000 раз! Н. Новгород, 2011 г. 6
Структуры данных во вторичной памяти Для того чтобы снизить время ожидания информация разделяется на несколько страниц одинакового размера, которые хранятся последовательно друг за другом в пределах одного цилиндра, и каждая операция чтения или записи работает сразу с несколькими страницами. Типичный размер страницы — от 211 до 214 байтов. Зачастую обработка прочитанной информации занимает меньше времени, чем ее поиск и чтение с диска. По этой причине в задачах, требующих активной работы с внешней памятью, главным компонентом времени работы будет являться количество обращений к диску, которое измеряется в терминах количества страниц информации, считанных с диска или записанных на него. Н. Новгород, 2011 г. 7
Структуры данных во вторичной памяти Типичный шаблон работы с объектом х, хранящемся на жёстком диске имеет следующий вид: T* x = (T*)malloc(sizeof(T)); Disk_Read(x); //Операции, обращающиеся и/или изменяющие поля х if(is. Changed(x)) { //Не требуется, если поля х не изменялись Disk_Write(x); } //Прочие операции, не изменяющие поля х Н. Новгород, 2011 г. 8
B-дерево В-деревья представляют собой сильно ветвящиеся сбалансированные деревья поиска, созданные специально для эффективной работы с дисковой памятью (и другими типами вторичной памяти с непосредственным доступом). Данный подход был предложен в 1972 году Р. Байером и Э. Мак-Крейтом, а также независимо от них М. Кауфманом. Н. Новгород, 2011 г. 9
B-дерево В-деревья - естественное обобщение бинарных деревьев поиска. На рис. 3 приведён пример В-дерева. Если внутренний узел В-дерева содержит n[х] ключей, то у него n[х] + 1 дочерних узлов. Ключи в узле х используются как разделители диапазона ключей, с которыми имеет дело данный узел, на n[х] + 1 поддиапазонов, каждый из которых относится к одному из дочерних узлов х. M D H B C F G Q T X J K L N P R S V W Y Z Рис. 3. В-дерево с согласными английского алфавита в качестве ключей Н. Новгород, 2011 г. 10
Определение В-дерева В-деревом c минимальной степенью t (t ≥ 2) называется дерево, удовлетворяющее свойствам: 1. Каждый внутренний узел, не являющийся корневым, имеет как минимум t дочерних узлов и не более 2 t дочерних узлов. 2. Корень, если не является листом, имеет минимум 2 -ух потомков. 3. Все листья расположены на одной и той же глубине, которая равна высоте дерева h. 4. Нелистовой узел с k потомками содержит k-1 ключей. Н. Новгород, 2011 г. 11
Определение В-дерева 5. Каждый узел х содержит следующие поля: а) n[х] , количество ключей, хранящихся в настоящий момент в узле х. б) Собственно ключи, количество которых равно n[х] и которые хранятся в невозрастающем порядке, так что key 1[x] ≤ key 2[х] ≤ … ≤ keyn[x][х]. в) Логическое значение leaf[х], равное TRUE, если х является листом, и FALSE, если х — внутренний узел. г) n[х] +1 указателей c 1[х], c 2[х] , . . . , cn[x]+1[х] на дочерние узлы. Н. Новгород, 2011 г. 12
Высота В-дерева Приведём оценку высоты В-дерева в наихудшем случае. Теорема. Высота В-дерева Т с n ≥ 1 узлами и минимальной степенью t ≥ 2 не превышает logt(n + 1)/2. Количество обращений к диску, необходимое для выполнения большинства операций с В-деревом, пропорционально его высоте. Асимптотические оценки высоты любого бинарного сбалансированного дерева, например АВЛ-дерева, и B-дерева совпадают, однако на практике высота B-дерева оказывается в несколько раз меньше за счёт выбора большого основания логарифма, что и объясняет использование B-деревьев в алгоритмах, работающих с внешней памятью. Н. Новгород, 2011 г. 13
Высота В-дерева Чтобы минимизировать количество выполняемых операций с диском Disk_Read и Disk_Write, желательно минимизировать их количество при работе с конкретным узлом B-дерева и за один раз считывать и записывать как можно больше информации. Размер узла В-дерева обычно соответствует дисковой странице, а количество потомков узла В-дерева ограничивается размером дисковой страницы. Для больших В-деревьев, хранящихся на диске, степень ветвления обычно находится между 50 и 2000, в зависимости от размера ключа относительно размера страницы. Большая степень ветвления резко снижает как высоту дерева, так и количество обращений к диску для поиска ключа. Н. Новгород, 2011 г. 14
Поиск в В-дереве Процедура B_Tree_Search(x, k) в качестве параметров получает указатель на корневой узел х поддерева и ключ k, который следует найти в этом поддереве. Выполняем линейный поиск наименьшего индекса i, такого что k ≤ kеу[х] (иначе i присваивается значение n[х]+1). 2. Если он найден, то возвращаем (x, i). 3. Процедура либо завершает свою работу неудачей (если х является листом), либо рекурсивно вызывает себя для поиска в соответствующем поддереве х (после выполнения чтения с диска необходимого дочернего узла, являющегося корнем исследуемого поддерева). 1. Процедура вернет упорядоченную пару (у, i), такую, что y – узел дерева, считанный в оперативную память, а keyi[y] = k. В противном случае процедура вернет значение NULL. Н. Новгород, 2011 г. 15
Поиск в В-дереве Количество дисковых страниц, к которым выполняется обращение процедурой B_Tree_Search, равно О(h) = О (logt n), где h — высота В-дерева, a n —количество содержащихся в нем узлов. Поскольку n[х] < 2 t, общее время вычислений составляет О (t·h) = O(t·logt n). M D H B C F G Q T X J K L N P R S V W Y Z Рис. 4. Запросы в B-дереве Зелёным цветом выделены узлы, просмотренные в процессе поиска ключа R. Н. Новгород, 2011 г. 16
Вставка ключа в В-дерево При вставке, как и в случае бинарных деревьев поиска, мы ищем позицию листа, в который будет вставлен новый ключ. Однако при работе с В-деревом мы не можем просто создать новый лист и вставить в него ключ, поскольку такое дерево не будет являться корректным В-деревом. Вместо этого мы всегда вставляем новый ключ в существующий лист. Поскольку вставить новый ключ в заполненный лист невозможно, мы вводим новую операцию — разбиение (splitting) заполненного (т. е. содержащего 2 t — 1 ключей) узла на два, каждый из которых содержит по t — 1 ключей. Н. Новгород, 2011 г. 17
Разбиение узла В-дерева Процедура B_Tree_Split_Child получает в качестве входного параметра незаполненный внутренний узел х (находящийся в оперативной памяти), индекс i и узел у (также находящийся в оперативной памяти), такой что у = сi[х] является заполненным дочерним узлом х. Процедура разбивает дочерний узел на два и соответствующим образом обновляет поля х, внося в него информацию о новом дочернем узле. Для разбиения заполненного корневого узла мы сначала делаем корень дочерним узлом нового пустого корневого узла, после чего можем использовать вызов B_Tree_Split_Child. При этом высота дерева увеличивается на 1. Разбиение — единственное средство увеличения высоты В-дерева. Н. Новгород, 2011 г. 18
Разбиение узла В-дерева keyi-1[x] keyi[x] keyi+1[x] x x … N W … y = c i[x] P Q R S T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 … N S W … y = c i[x] z = ci+1[x] P Q R T U V T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 Рис. 5. Разбиение узла с минимальной степенью t = 4 Заполненный узел у разбивается медианой S, которая перемещается в родительский узел. Те ключи из у, которые больше медианы, помещаются в новый узел z, который становится новым дочерним узлом х. Н. Новгород, 2011 г. 19
Вставка ключа в незаполненный узел Вспомогательная процедура B_Tree_Insert_Nonfull(x, k) вставляет ключ k в узел х, который должен быть незаполненным при вызове процедуры. 1. Если х является листом, тогда ключ k просто вставляется в данный лист х путём отыскания позиции вставки этого ключа. После изменения полей этого узла выполняем дисковую операцию Disk_Write(x). Иначе 1) Определяем дочерний узел х, в который спустится рекурсия (пусть это узел ci[x]). 2) Выполняем дисковую операцию Disk_Read(ci[x]). 3) Если узел ci[x] заполнен, тогда разбиваем его на два незаполненных узла путём вызова процедуры B_Tree_Split_Child(x, i, ci[x]) и определяем, в какой из двух получившихся в результате разбиения узлов должна спуститься рекурсия. 4) Рекурсивно вызываем процедуру B_Tree_Insert_Nonfull(ci[x], k) для вставки k в соответствующее поддерево. Н. Новгород, 2011 г. 20
Вставка ключа в В-дерево Для вставки ключа k в B-дерево Т высоты h мы воспользуемся процедурой B_Tree_Insert(T, k). 1. r ← root[T] 2. Если корень дерева T заполнен, т. е. n[r] = 2 t-l, тогда: 1) Выделяем дисковую страницу для нового узла s. 2) root[T] ← s , leaf[s] ← FALSE, n[s] ← 0, ci [s] ← r Заполняем поля нового узла s. Узел s (у которого два дочерних узла) делаем новым корнем В-дерева. 3) Вызываем процедуру B_Tree_Split_Child(s, 1, r) для гарантии того, что рекурсия никогда не столкнется с заполненным узлом. 4) Вызываем процедуру B_Tree_Insert_Nonfull(s, k), выполняющую вставку ключа k в дерево с незаполненным корнем s. Иначе вызываем процедуру B_Tree_Insert_Nonfull (r, k), выполняющую вставку ключа k в дерево с незаполненным корнем r. Н. Новгород, 2011 г. 21
Вставка ключа в В-дерево Вставка ключа k в В-дерево T высоты h выполняется за один нисходящий проход по дереву, требующий О(h) обращений к диску. Необходимое процессорное время составляет О(t·h) = О(t·logtn). Разбиение корня (рис. 9) — единственный путь увеличения высоты В-дерева. В отличие от бинарных деревьев поиска, у В-деревьев высота увеличивается "сверху", а не "снизу". root[T] s H root[T] r A D F H L N P r A D F L N P T 1 T 2 T 3 T 4 T 5 T 6 T 7 T 8 Рис. 9 Разбиение корня с минимальной степенью t = 4 Н. Новгород, 2011 г. 22
Вставка ключа в В-дерево G A B C D E J K M P N O T X Q R S U V Y Z вставка L P G M A B C D E J K L T X N O Q R S U V Y Z Рис. 10. Вставка ключа L в В-дерево с минимальной степенью 3. Узлы, изменяемые в процессе вставки, выделены зелёным цветом. В данном примере происходит образование нового корня, а с корнем исходного дерева происходит расщепление. Н. Новгород, 2011 г. 23
Удаление ключа из В-дерева Мы должны обеспечить, чтобы при выполнении операции удаления узел не становится слишком мало заполнен (за исключением корневого узла, который может иметь менее t — 1 ключей). Итак, пусть процедура B_Tree_Delete должна удалить ключ k из поддерева, корнем которого является узел х. При её рекурсивном вызове для узла х гарантировано наличие в этом узле по крайней мере t ключей. Это условие требует наличия в узле большего количества ключей, чем минимальное в обычном В-дереве, так что иногда ключ может быть перемещен в дочерний узел перед тем, как рекурсия обратится к этому дочернему узлу. Такое ужесточение свойства В-дерева (наличие "запасного" ключа) дает нам возможность выполнить удаление ключа за один нисходящий проход по дереву. Н. Новгород, 2011 г. 24
Удаление ключа из В-дерева Рассмотрим различные возникающие при удалении ключа ситуации. 1. Если ключ k находится в узле х и х является листом — удаляем k из х. P C G M удаление F T X х A B D E F J K L N O Q R S U V Y Z P C G M T X х A B D E J K L N O Q R S U V Y Z Рис. 11. Удаление узла F из В-дерева с минимальной степенью 3. Модифицируемые узлы показаны зелёным цветом. Н. Новгород, 2011 г. 25
Удаление ключа из В-дерева 2. а) Если дочерний по отношению к внутреннему узлу х узел у, предшествующий ключу k в узле х, содержит не менее t ключей, то находим k` — предшественника k в поддереве, корнем которого является у. Рекурсивно удаляем k` и заменяем k в х ключом k`. P х C G удаление M T X M y A B D E J K L N O Q R S U V Y Z P k` (L – предшественник M) х C G T X L y A B D E J K N O Q R S U V Y Z Рис. 12. Удаление узла M из В-дерева с минимальной степенью 3. Модифицируемые узлы показаны зелёным цветом. Н. Новгород, 2011 г. 26
Удаление ключа из В-дерева 2. б) Если дочерний по отношению к х узел z, следующий за ключом k в узле х, содержит не менее t ключей, то находим k`— следующий за k ключ в поддереве, корнем которого является z. Рекурсивно удаляем k` и заменяем k в х ключом k`. P х C G удаление L T X L z A B D E F J K M Q R S N O U V Y Z P k` (L – предшественник M) х C G T X M z A B D E F J K N O Q R S U V Y Z Рис. 13. Удаление узла L из В-дерева с минимальной степенью 3. Модифицируемые узлы показаны зелёным цветом. Н. Новгород, 2011 г. 27
Удаление ключа из В-дерева 2. в) Если и у, и z содержат по t — 1 ключей, вносим k и все ключи z в у (при этом из х удаляется k и указатель на z, а узел у после этого содержит 2 t — 1 ключей), а затем освобождаем z и рекурсивно удаляем k из у. P х C y A B G удаление G T X L z D E J K N O Q R S U V Y Z P х C L T X y A B D E J K N O Q R S U V Y Z Рис. 14. Удаление узла G из В-дерева с минимальной степенью 3. Модифицируемые узлы показаны зелёным цветом. Н. Новгород, 2011 г. 28
Удаление ключа из В-дерева 3. Если ключ k отсутствует во внутреннем узле х, находим корень сi[х] поддерева, которое должно содержать k (если таковой ключ имеется в данном В-дереве). Если сi[х] содержит только t — 1 ключей, выполняем шаг 3. а или 3. б для того, чтобы гарантировать, что далее мы переходим в узел, содержащий как минимум t ключей. Затем мы рекурсивно удаляем k из поддерева с корнем сi[х]. Н. Новгород, 2011 г. 29
Удаление ключа из В-дерева 3. а) Если сi[х] содержит только t — 1 ключей, но при этом один из его непосредственных соседей (под которым мы понимаем дочерний по отношению к х узел, отделенный от рассматриваемого ровно одним ключом-разделителем) содержит как минимум t ключей, тогда: 1. Передадим в сi[х] ключ-разделитель между данным узлом и его непосредственным соседом из х. 2. На его место поместим крайний ключ из соседнего узла и перенесем соответствующий указатель из соседнего узла в сi[х]. 3. б) Если сi[х] и оба его непосредственных соседа содержат по t — 1 ключей, объединим сi[х] с одним из его соседей (при этом бывший ключ-разделитель из х станет медианой нового узла). Н. Новгород, 2011 г. 30
Удаление ключа из В-дерева Поскольку большинство ключей в В-дереве находится в листьях, можно ожидать, что на практике чаще всего удаления будут выполняться из листьев. Процедура B_Tree_Delete в этом случае выполняется за один нисходящий проход по дереву, без возвратов. Однако при удалении ключа из внутреннего узла процедуре может потребоваться возврат к узлу, ключ из которого был удален и замещен его предшественником или последующим за ним ключом (случаи 2. а и 2. б). Процедура B_Tree_Delete требует всего лишь О(h) дисковых операций для дерева высотой h, поскольку между рекурсивными вызовами процедуры выполняется только О(1) вызовов процедур Disk_Read или Disk_Write. Необходимое процессорное время составляет О (t·h) = O(t·logt n). Н. Новгород, 2011 г. 31
Применение и разновидности B-деревьев
Применение и разновидности В-деревьев B-деревья и их разновидности широко используются при проектировании современных файловых систем. Широкое применение B-деревья получили в различных СУБД для организации индексирования по ключевым полям. В файловой системе NTFS B-деревья используются для оптимизации поиска файлов и структурирования метаинформации файла. В файловых системах HFS и Reiser 4 для организации работы файловой системы используется B*-дерево. В этой разновидности B-дерева узлы дерева должны быть заполнены не менее чем на 2/3 от максимальной заполненности. Это ограничение приводит к более эффективному использованию места на жёстком диске. Н. Новгород, 2011 г. 33
Применение и разновидности В-деревьев Ннаиболее распространенный механизм, используемый в файловой системе XFS для увеличения масштабируемости – это B+ деревья. B+ дерево представляют собой B-дерево, внутренние узлы которого хранят только данные о структуре дерева, а листья содержат указатели на страницы, хранящие данные. Это позволяет увеличить степень ветвления для внутренних узлов и хранить структуру B+ дерева отдельно от данных. B+ деревья используются для отслеживания свободного пространства, для доступа к непрерывным частям файла. В B+ деревья выстраиваются элементы каталога (вместо образования обычных линейных списков). Н. Новгород, 2011 г. 34
Программная реализация и результаты экспериментов
Особенности реализации Чтобы провести наиболее объективное сравнение различных типов деревьев по работе с внешней памятью мы реализовали тестовую систему, позволяющую абстрагировать работу внешней памяти от алгоритмов работы с деревьями с помощью интерфейса внешнего хранилища IStorage. class IStorage { public: virtual I Allocate() = 0; virtual void Free(I index) = 0; virtual void Read(I index, T* value) = 0; virtual void Write(I index, const T* value) = 0; }; Н. Новгород, 2011 г. 36
Особенности реализации Интерфейс предоставляет возможность запросить страницу с диска с указанным номером, записать обратно, выделить новую страницу и сохранить её обратно. Используя данную абстракцию мы реализовали дисковое хранилище, предоставляющее произвольный доступ к большому файлу на диске, хранилище в оперативной памяти, а также хранилище, позволяющее считать число обращений к диску. Важным условием было то, что все алгоритмы хранят свои данные во внешней памяти за исключением корневого элемента, которые всегда находится в оперативной памяти. Н. Новгород, 2011 г. 37
Реализованные деревья В рамках нашей работы мы реализовали следующие типы деревьев: q B-дерево q B+-дерево q АВЛ-дерево q Бинарное дерево Н. Новгород, 2011 г. 38
Сравнение деревьев Для тестирования использовались следующие параметры: q Размер записи 100 байт q Размер ключа 8 байт q Число записей от 1 до 1000000 q Параметр t = 20 Для тестирования генерировался случайный набор данных с уникальными ключами. Добавление и поиск ключей производились в случайном порядке. Н. Новгород, 2011 г. 39
Вставка элемента – число чтений Число узлов в дереве Н. Новгород, 2011 г. 40
Вставка элемента – число записей Число узлов в дереве Н. Новгород, 2011 г. 41
Поиск элемента – число чтений Число узлов в дереве Н. Новгород, 2011 г. 42
Поиск элемента – число чтений ticks Число узлов в дереве Н. Новгород, 2011 г. 43
Литература Алгоритмы: построение и анализ. Томас Кормен, Чарльз Лейзерсон, Рональд Ривест, Клиффорд Штайн — Москва-СПб-Киев: Вильямс. 2005. — 1296 с. 2. Искусство программирования, том 3. Сортировка и поиск. Дональд Э. Кнут — М. : Вильямс. 2000. — 824 с. 3. Структуры данных и алгоритмы Ахо А. , Хопкрофт Дж. , Ульман Дж. 4. http: //www. opennet. ru/docs/RUS/xfs_arch/ 1. 44
6946733fb0b02652b5245f2344a93fab.ppt