HTML Diff
0 added 8 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Ранее в курсе мы уже познакомились со сбалансированными деревьями поиска - это АВЛ-деревья или красно-черные деревья.</p>
1 <p>Ранее в курсе мы уже познакомились со сбалансированными деревьями поиска - это АВЛ-деревья или красно-черные деревья.</p>
2 <p>Они способны организовать эффективный поиск элементов за время logN. Но чтобы совершить этот поиск, потребуется выполнить logN операций. Со временем число данных, которые нужно хранить в дереве, будет расти. Дерево потребуется сохранять на более дешевых хранилищах данных вроде жестких дисков, а не хранить в оперативной памяти.</p>
2 <p>Они способны организовать эффективный поиск элементов за время logN. Но чтобы совершить этот поиск, потребуется выполнить logN операций. Со временем число данных, которые нужно хранить в дереве, будет расти. Дерево потребуется сохранять на более дешевых хранилищах данных вроде жестких дисков, а не хранить в оперативной памяти.</p>
3 <p>Это ведет к тому, что для поиска элемента в дереве нам придется logN раз обратиться к жесткому диску. При больших N это число операций остается существенным. Операции над жестким диском гораздо медленнее, чем над оперативной памятью. Значит, оборудование будет быстро изнашиваться, а само приложение будет работать медленнее.</p>
3 <p>Это ведет к тому, что для поиска элемента в дереве нам придется logN раз обратиться к жесткому диску. При больших N это число операций остается существенным. Операции над жестким диском гораздо медленнее, чем над оперативной памятью. Значит, оборудование будет быстро изнашиваться, а само приложение будет работать медленнее.</p>
4 <p>Ученым предстояло решить, как организовать эффективный поиск в древовидных структурах, которые должны храниться в постоянной памяти. Чтобы решить эту задачу, они придумали новый вид деревьев - сильноветвящиеся B-деревья и их производные: B+ деревья, B*-деревья, 2-3 деревья.</p>
4 <p>Ученым предстояло решить, как организовать эффективный поиск в древовидных структурах, которые должны храниться в постоянной памяти. Чтобы решить эту задачу, они придумали новый вид деревьев - сильноветвящиеся B-деревья и их производные: B+ деревья, B*-деревья, 2-3 деревья.</p>
5 <p>В этом уроке мы детально познакомимся с видами сильноветвящихся деревьев и особенностями реализации операций поиска, вставки и удаления узлов.</p>
5 <p>В этом уроке мы детально познакомимся с видами сильноветвящихся деревьев и особенностями реализации операций поиска, вставки и удаления узлов.</p>
6 <h2>Устройство B-деревьев</h2>
6 <h2>Устройство B-деревьев</h2>
7 <p>Рассмотрим дерево, которое состоит из миллиона элементов. Если хранить данные в сбалансированном бинарном дереве поиска, то поиск элемента займет двадцать операций:</p>
7 <p>Рассмотрим дерево, которое состоит из миллиона элементов. Если хранить данные в сбалансированном бинарном дереве поиска, то поиск элемента займет двадцать операций:</p>
8 <p>log2(1000000) = 19,932</p>
8 <p>log2(1000000) = 19,932</p>
9 <p>Если разбить дерево на страницы, в каждой из которых будет по семь узлов дерева, поиск поддерева с нужным элементом будет выполнен за семь обращений к диску:</p>
9 <p>Если разбить дерево на страницы, в каждой из которых будет по семь узлов дерева, поиск поддерева с нужным элементом будет выполнен за семь обращений к диску:</p>
10 <p>log(7+1) 1000000 = 6,644</p>
10 <p>log(7+1) 1000000 = 6,644</p>
11 <p>Рассмотрим пример такого разбиения:</p>
11 <p>Рассмотрим пример такого разбиения:</p>
12 <p>Такая группировка узлов на страницах превращает бинарное дерево в октарное - с восемью дочерними узлами. Число дочерних узлов у страницы может и дальше увеличиваться, например, до 128 элементов. Это приведет к тому, что поиск элемента будет выполнен всего за три обращения к диску:</p>
12 <p>Такая группировка узлов на страницах превращает бинарное дерево в октарное - с восемью дочерними узлами. Число дочерних узлов у страницы может и дальше увеличиваться, например, до 128 элементов. Это приведет к тому, что поиск элемента будет выполнен всего за три обращения к диску:</p>
13 <p>log128(1000000) = 2,847</p>
13 <p>log128(1000000) = 2,847</p>
14 <p>Таким образом, нам достаточно выбрать оптимальный размер страницы, который удовлетворяет нашим ограничениям по оперативной памяти. Также мы можем хранить в оперативной памяти ссылку на корневой элемент дерева, а сам вид дерева можно привести к виду на рисунке:</p>
14 <p>Таким образом, нам достаточно выбрать оптимальный размер страницы, который удовлетворяет нашим ограничениям по оперативной памяти. Также мы можем хранить в оперативной памяти ссылку на корневой элемент дерева, а сам вид дерева можно привести к виду на рисунке:</p>
15 <p>В этом случае в узлах располагается несколько ключей в порядке возрастания слева направо. Дочерние узлы содержат ключи, которые находятся в промежутке между значениями родительского узла.</p>
15 <p>В этом случае в узлах располагается несколько ключей в порядке возрастания слева направо. Дочерние узлы содержат ключи, которые находятся в промежутке между значениями родительского узла.</p>
16 <p>Помимо особенности расположения ключей стоит отметить, что B-дерево со степенью m обладает следующими свойствами:</p>
16 <p>Помимо особенности расположения ключей стоит отметить, что B-дерево со степенью m обладает следующими свойствами:</p>
17 <ul><li>Глубина всех листьев одинакова</li>
17 <ul><li>Глубина всех листьев одинакова</li>
18 <li>Каждый узел имеет не более m потомков</li>
18 <li>Каждый узел имеет не более m потомков</li>
19 <li>Каждый узел кроме корневого и листовых имеют не менее m/2 потомков</li>
19 <li>Каждый узел кроме корневого и листовых имеют не менее m/2 потомков</li>
20 <li>Если высота дерева больше единицы, то у корневого узла не менее двух дочерних узлов</li>
20 <li>Если высота дерева больше единицы, то у корневого узла не менее двух дочерних узлов</li>
21 <li>Нелистовой узел с k потомками имеет k-1 ключей</li>
21 <li>Нелистовой узел с k потомками имеет k-1 ключей</li>
22 </ul><p>Представим узел B-дерева в виде кода на JavaScript:</p>
22 </ul><p>Представим узел B-дерева в виде кода на JavaScript:</p>
23 - <p><strong>Javascript</strong></p>
 
24 - <p><strong>Java</strong></p>
 
25 - <p><strong>Python</strong></p>
 
26 - <p><strong>PHP</strong></p>
 
27 <p>Над B-деревьями выполняются все классические операции:</p>
23 <p>Над B-деревьями выполняются все классические операции:</p>
28 <ul><li>Поиск узла</li>
24 <ul><li>Поиск узла</li>
29 <li>Добавление узла</li>
25 <li>Добавление узла</li>
30 <li>Удаление узла</li>
26 <li>Удаление узла</li>
31 </ul><p>Однако сложная организация ветвления узлов вносит свои реализации. Далее рассмотрим эти операции подробнее.</p>
27 </ul><p>Однако сложная организация ветвления узлов вносит свои реализации. Далее рассмотрим эти операции подробнее.</p>
32 <h2>Операции над B-деревьями</h2>
28 <h2>Операции над B-деревьями</h2>
33 <p>Поиск узлов в случае работы с B-деревом отличается от аналогичной операции над бинарным деревом только порядком действий во время проверки ключей узла. Он будет выглядеть следующим образом:</p>
29 <p>Поиск узлов в случае работы с B-деревом отличается от аналогичной операции над бинарным деревом только порядком действий во время проверки ключей узла. Он будет выглядеть следующим образом:</p>
34 - <p><strong>Javascript</strong></p>
 
35 - <p><strong>Java</strong></p>
 
36 - <p><strong>Python</strong></p>
 
37 - <p><strong>PHP</strong></p>
 
38 <p>В этом примере можно заметить пересечение с логикой поиска в бинарном дереве поиска. Если искомое значение меньше текущего значения узла, мы переходим по левой от него ветке. Если больше всех ключей узла - переходим на правое поддерево.</p>
30 <p>В этом примере можно заметить пересечение с логикой поиска в бинарном дереве поиска. Если искомое значение меньше текущего значения узла, мы переходим по левой от него ветке. Если больше всех ключей узла - переходим на правое поддерево.</p>
39 <p>Вставка новых элементов в B-дерево разрешается только в листовые узлы. Это приводит к необходимости следить за заполненностью узлов. Если при вставке превысилась максимальная степень узла, то узел нужно разделять на части.</p>
31 <p>Вставка новых элементов в B-дерево разрешается только в листовые узлы. Это приводит к необходимости следить за заполненностью узлов. Если при вставке превысилась максимальная степень узла, то узел нужно разделять на части.</p>
40 <p>Чтобы разделить узел, создается два новых листовых узла, которые содержат половину значений из существующего узла, и переносится среднее значение в родительский узел.</p>
32 <p>Чтобы разделить узел, создается два новых листовых узла, которые содержат половину значений из существующего узла, и переносится среднее значение в родительский узел.</p>
41 <p>Если заполнен и корневой узел, то мы разбиваем его на два новых: выбираем среднее значение, создаем для него новый узел, который станет новым корнем всего дерева:</p>
33 <p>Если заполнен и корневой узел, то мы разбиваем его на два новых: выбираем среднее значение, создаем для него новый узел, который станет новым корнем всего дерева:</p>
42 <p>В данном примере мы вставляем ключ 3 в дерево с одним заполненным корневым узлом. Вставка ключа приводит к переполнению корневого узла. Чтобы решить эту ситуацию, мы разбиваем корневой узел на две части, создаем новый узел со средним значением и делаем его корнем всего дерева.</p>
34 <p>В данном примере мы вставляем ключ 3 в дерево с одним заполненным корневым узлом. Вставка ключа приводит к переполнению корневого узла. Чтобы решить эту ситуацию, мы разбиваем корневой узел на две части, создаем новый узел со средним значением и делаем его корнем всего дерева.</p>
43 <p>Аналогично изменение структуры узлов может возникнуть, когда узел удаляется. Если в соседних узлах слишком мало ключей - занятость узлов падает ниже значения m/2 - и эти узлы находятся на одном родственном уровне, то такие узлы необходимо слить. Данная ситуация называется<strong>антипереполнением</strong>.</p>
35 <p>Аналогично изменение структуры узлов может возникнуть, когда узел удаляется. Если в соседних узлах слишком мало ключей - занятость узлов падает ниже значения m/2 - и эти узлы находятся на одном родственном уровне, то такие узлы необходимо слить. Данная ситуация называется<strong>антипереполнением</strong>.</p>
44 <p>Существует два способа объединения узлов:</p>
36 <p>Существует два способа объединения узлов:</p>
45 <ul><li>Если у двух смежных узлов общий предок, и их содержимые помещаются в один узел, их следует объединить</li>
37 <ul><li>Если у двух смежных узлов общий предок, и их содержимые помещаются в один узел, их следует объединить</li>
46 <li>Если содержимое смежных узлов не помещается в одном узле, то ключи перераспределяются между ними, чтобы восстановить баланс</li>
38 <li>Если содержимое смежных узлов не помещается в одном узле, то ключи перераспределяются между ними, чтобы восстановить баланс</li>
47 </ul><p>Операции слияния и расщепления достаточно дорогостоящие. Чтобы оптимизировать данный процесс, были придуманы специальные подвиды B-деревьев.</p>
39 </ul><p>Операции слияния и расщепления достаточно дорогостоящие. Чтобы оптимизировать данный процесс, были придуманы специальные подвиды B-деревьев.</p>
48 <h2>Производные виды деревьев</h2>
40 <h2>Производные виды деревьев</h2>
49 <p>К производным видам B-деревьев чаще всего относят следующие три вида:</p>
41 <p>К производным видам B-деревьев чаще всего относят следующие три вида:</p>
50 <ul><li>B⁺-деревья</li>
42 <ul><li>B⁺-деревья</li>
51 <li>B*-деревья</li>
43 <li>B*-деревья</li>
52 <li>2-3-деревья</li>
44 <li>2-3-деревья</li>
53 </ul><h3>B⁺-деревья</h3>
45 </ul><h3>B⁺-деревья</h3>
54 <p>В B⁺-деревьях данные ключей хранятся только в листовых узлах, а в промежуточных хранятся копии значений ключей. Это помогает эффективно организовывать поиск в блочных средах хранения. Например, в файловых системах, где B⁺-деревья применяют, чтобы хранить каталоги.</p>
46 <p>В B⁺-деревьях данные ключей хранятся только в листовых узлах, а в промежуточных хранятся копии значений ключей. Это помогает эффективно организовывать поиск в блочных средах хранения. Например, в файловых системах, где B⁺-деревья применяют, чтобы хранить каталоги.</p>
55 <p>Также реляционные системы управления базами данных, такие как DB2, Informix, Microsoft SQL Server, Oracle, поддерживают этот тип деревьев, чтобы организовывать табличные индексы.</p>
47 <p>Также реляционные системы управления базами данных, такие как DB2, Informix, Microsoft SQL Server, Oracle, поддерживают этот тип деревьев, чтобы организовывать табличные индексы.</p>
56 <h3>B*-деревья</h3>
48 <h3>B*-деревья</h3>
57 <p>B*-дерево ориентировано на более плотное хранение ключей во внутренних узлах. Этот вариант гарантирует, что некорневые узлы заполнены как минимум на две трети от m вместо половины от m.</p>
49 <p>B*-дерево ориентировано на более плотное хранение ключей во внутренних узлах. Этот вариант гарантирует, что некорневые узлы заполнены как минимум на две трети от m вместо половины от m.</p>
58 <p>B*-деревья используются для того, чтобы вызывать дорогостоящую операцию разделения как можно реже. Чтобы достичь этого, вместо немедленного разделения узла при его заполнении, его ключи "переливаются" в соседний узел. Если же и соседний узел заполнен, то ключи приблизительно поровну разделяются на три новых узла.</p>
50 <p>B*-деревья используются для того, чтобы вызывать дорогостоящую операцию разделения как можно реже. Чтобы достичь этого, вместо немедленного разделения узла при его заполнении, его ключи "переливаются" в соседний узел. Если же и соседний узел заполнен, то ключи приблизительно поровну разделяются на три новых узла.</p>
59 <h3>2-3-деревья</h3>
51 <h3>2-3-деревья</h3>
60 <p>2-3 деревья - частный случай B⁺-деревьев с сокращенным количеством элементов на страницах. Каждый узел 2-3-дерева, кроме листовых, может содержать только один ключ и два потомка, либо два ключа и три потомка.</p>
52 <p>2-3 деревья - частный случай B⁺-деревьев с сокращенным количеством элементов на страницах. Каждый узел 2-3-дерева, кроме листовых, может содержать только один ключ и два потомка, либо два ключа и три потомка.</p>
61 <h2>Выводы</h2>
53 <h2>Выводы</h2>
62 <p>Способ хранения деревьев может оказывать существенное влияние на организацию структуры узлов и входящих в них ключей. Например, хранение классических бинарных деревьев поиска на жестком диске может привести к многократному росту числа операций над диском.</p>
54 <p>Способ хранения деревьев может оказывать существенное влияние на организацию структуры узлов и входящих в них ключей. Например, хранение классических бинарных деревьев поиска на жестком диске может привести к многократному росту числа операций над диском.</p>
63 <p>Для оптимизации хранения и поиска данных в таких условиях были предложены B-деревья. Они помогают сократить нагрузку на диск. Это возможно, если увеличить количество веток в узлах и хранить несколько ключей полезной нагрузки.</p>
55 <p>Для оптимизации хранения и поиска данных в таких условиях были предложены B-деревья. Они помогают сократить нагрузку на диск. Это возможно, если увеличить количество веток в узлах и хранить несколько ключей полезной нагрузки.</p>