Главная страница
Навигация по странице:

  • 4.7.1. Сильно ветвящиеся Б=деревья

  • Алгоритмы и структуры данныхНовая версия для Оберона cdмосква, 2010Никлаус ВиртПеревод с английского под редакцией


    Скачать 2.67 Mb.
    НазваниеАлгоритмы и структуры данныхНовая версия для Оберона cdмосква, 2010Никлаус ВиртПеревод с английского под редакцией
    Анкор11221
    Дата18.05.2023
    Размер2.67 Mb.
    Формат файлаpdf
    Имя файлаAlgoritmy_i_struktury_dannyh.pdf
    ТипКнига
    #1142198
    страница19 из 22
    1   ...   14   15   16   17   18   19   20   21   22

    4.7. БGдеревья (BGtrees)
    До сих пор наше обсуждение ограничивалось двоичными деревьями, то есть таки%
    ми, где каждый узел имеет не более двух потомков. Этого вполне достаточно,
    если, например, мы хотим представить семейные связи, подчеркивая происхож%
    дение, то есть указывая для каждого человека его двух родителей. Ведь ни у кого не бывает больше двух родителей. Но что, если возникнет необходимость указы%
    вать потомков каждого человека? Тогда придется учесть, что у некоторых людей больше двух детей, и тогда деревья будут содержать узлы со многими ветвями.
    Будем называть такие деревья сильно ветвящимися.
    Разумеется, в подобных структурах нет ничего особенного, и мы уже знакомы со всеми средствами программирования и определения данных, чтобы справиться с такими ситуациями. Например, если задан абсолютный верхний предел на число детей (такое предположение, конечно, немного футуристично), то можно представ%
    лять детей полем%массивом в записи, представляющей человека. Но если число де%
    тей сильно зависит от конкретного индивида, то это может привести к плохому ис%
    пользованию имеющейся памяти. В этом случае гораздо разумнее организовать потомство с помощью линейного списка, причем указатель на младшего (или стар%
    шего) потомка хранится в записи родителя. Возможное определение типа для этого случая приводится ниже, а возможная структура данных показана на рис. 4.40.
    TYPE Person = POINTER TO RECORD
    name: alfa;
    sibling, offspring: Person
    END
    Б<деревья (B

    Динамические структуры данных
    228
    При наклоне картинки на 45 градусов она будет выглядеть как обычное дво%
    ичное дерево. Но такой вид вводит в заблуждение, так как функциональный смысл двух структур совершенно разный. Обычно нельзя безнаказанно обра%
    щаться с братом как с сыном и не следует так поступать даже при определении структур данных. Структуру данных в этом примере можно усложнить еще больше, вводя в запись для каждого индивида дополнительные компоненты,
    представляющие другие семейные связи, например супружеские отношения между мужем и женой или даже информацию о родителях, ведь подобную ин%
    формацию не всегда можно получить из ссылок от родителей к детям и к брать%
    ям%сестрам. Подобная структура быстро вырастает в сложную реляционную базу данных, на которую можно отобразить несколько деревьев. Алгоритмы, ра%
    ботающие с такими структурами, тесно связаны с соответствующими определе%
    ниями данных, так что не имеет смысла указывать ни какие%либо общие прави%
    ла, ни общеприменимые приемы.
    Однако есть очень полезная область применения сильно ветвящихся деревьев,
    представляющая общий интерес. Это построение и поддержка больших деревьев поиска, в которых нужно выполнять вставки и удаления элементов, но опера%
    тивная память компьютера либо недостаточна по размеру, либо слишком дорога,
    чтобы использовать ее для длительного хранения данных.
    Итак, предположим, что узлы дерева должны храниться на внешних устрой%
    ствах хранения данных, таких как диски. Динамические структуры данных,
    введенные в этой главе, особенно удобны для того, чтобы использовать внешние носители. Главная новация будет просто в том, что роль указателей теперь будут играть дисковые адреса, а не адреса в оперативной памяти. При использовании двоичного дерева для набора данных из, скажем, миллиона элементов потребу%
    ется в среднем примерно ln 10 6
    (то есть около 20) шагов поиска. Поскольку здесь каждый шаг требует обращения к диску (с неизбежной задержкой), то крайне желательно организовать хранение так, чтобы уменьшить число таких обраще%
    ний. Сильно ветвящееся дерево – идеальное решение проблемы. Если имеет мес%
    то обращение к элементу на внешнем носителе, то без особых усилий становится доступной целая группа элементов. Отсюда идея разделить дерево на поддеревья таким образом, чтобы эти поддеревья представлялись блоками, которые доступ%
    Рис. 4.40. Сильно ветвящееся дерево, представленное как двоичное дерево

    229
    ны сразу целиком. Назовем такие поддеревья страницами. На рис. 4.41 показано двоичное дерево, разделенное на страницы по 7 узлов на каждой.
    Рис. 4.41. Двоичное дерево, разделенное на страницы
    Экономия на числе обращений к диску – теперь обращение к диску соответ%
    ствует обращению к странице – может быть значительной. Предположим, что на каждой странице решено размещать по 100 узлов (это разумное число); тогда де%
    рево поиска из миллиона элементов потребует в среднем только log
    100
    (10 6
    )
    (то есть около 3) обращений к страницам вместо 20. Правда, при случайном росте де%
    рева это число все равно может достигать
    10 4
    в наихудшем случае. Ясно, что рос%
    том сильно ветвящихся деревьев почти обязательно нужно как%то управлять.
    4.7.1. Сильно ветвящиеся Б=деревья
    Если искать способ управления ростом дерева, то следует сразу исключить требо%
    вание идеальной сбалансированности, так как здесь понадобятся слишком боль%
    шие накладные расходы на балансировку. Ясно, что нужно несколько ослабить требования. Весьма разумный критерий был предложен Бэйером и Маккрейтом в 1970 г. [4.2]: каждая страница (кроме одной) содержит от n
    до
    2n узлов, где n

    некоторая заданная константа. Поэтому для дерева из
    N
    элементов и с максималь%
    ным размером страницы в
    2n узлов потребуется в худшем случае log n
    N
    обраще%
    ний к страницам; а именно на обращения к страницам приходятся основные уси%
    лия в подобном поиске. Более того, важный коэффициент использования памяти будет не меньше 50%, так как страницы всегда будут заполнены по крайней мере наполовину. Причем при всех этих достоинствах описываемая схема требует сравнительно простых алгоритмов для поиска, вставки и удаления элементов.
    В дальнейшем мы подробно изучим их.
    Такую структуру данных называют Бдеревом (также B%дерево; B%tree), число n
    – его порядком, и при этом предполагаются следующие свойства:
    1. Каждая страница содержит не более
    2n элементов (ключей).
    2. Каждая страница, кроме корневой, содержит не менее n
    элементов.
    Б<деревья (B

    Динамические структуры данных
    230 3. Каждая страница либо является концевой, то есть не имеет потомков, либо имеет m+1
    потомков, где m
    – число ключей на этой странице.
    4. Все концевые страницы находятся на одном уровне.
    Рисунок 4.42 показывает Б%дерево порядка 2, имеющее 3 уровня. На всех стра%
    ницах – 2, 3 или 4 элемента; исключением является корень, где разрешено иметь только один элемент. Все концевые страницы – на уровне 3. Ключи будут стоять в порядке возрастания слева направо, если Б%дерево «сплющить» в один слой так,
    чтобы потомки вставали между ключами соответствующих страниц%предков. Та%
    кая организация является естественным развитием идеи двоичных деревьев поис%
    ка и предопределяет способ поиска элемента с заданным ключом. Рассмотрим страницу, показанную на рис. 4.43, и пусть задан аргумент поиска x
    . Предполагая,
    что страница уже считана в оперативную память, можно использовать обычные методы поиска среди ключей k
    0
    ... k m–1
    . Если m
    велико, то можно применить по%
    иск делением пополам; если оно мало, то будет достаточно обычного последова%
    тельного поиска. (Заметим, что время поиска в оперативной памяти будет, веро%
    ятно, пренебрежимо мало по сравнению со временем считывания страницы в оперативную память из внешней.) Если поиск завершился неудачей, то возника%
    ет одна из следующих ситуаций:
    1.
    k i
    < x < k i+1
    для
    0 < i < m–1
    Поиск продолжается на странице p
    i
    ^
    2.
    k m–1
    < x
    Поиск продолжается на странице p
    m–1
    ^
    3.
    x < k
    0
    Поиск продолжается на странице p
    –1
    ^
    Рис. 4.42. Б:дерево порядка 2
    Рис. 4.43. Б:дерево с m ключами

    231
    Если в каком%то случае указатель оказался равен
    NIL
    , то есть страница%пото%
    мок отсутствует, то это значит, что элемента с ключом x
    во всем дереве нет, и по%
    иск заканчивается.
    Удивительно, но вставка в Б%дерево тоже сравнительно проста. Если элемент нужно вставить на странице с m < 2n элементами, то вставка затрагивает только содержимое этой страницы. И только вставка в уже заполненную страницу затро%
    нет структуру дерева и может привести к размещению новых страниц. Чтобы по%
    нять, что случится в этом случае, рассмотрим рис. 4.44, который показывает вставку ключа 22 в Б%дерево порядка 2. Здесь выполняются следующие шаги:
    1. Обнаруживается, что ключа 22 в дереве нет; вставка на странице
    C
    невоз%
    можна, так как
    C
    уже полна.
    2. Страница
    C
    разбивается на две (то есть размещается новая страница
    D
    ).
    3.
    2n+1
    ключей поровну распределяются между страницами
    C
    и
    D
    , а средний ключ перемещается на один уровень вверх на страницу%предок
    A
    Рис. 4.44. Вставка ключа 22 в Б:дерево
    Эта очень изящная схема сохраняет все характеристические свойства Б%дере%
    вьев. В частности, разбиваемые страницы содержат в точности n
    элементов. Разу%
    меется, вставка элемента на странице%предке тоже может вызвать переполнение,
    так что снова нужно выполнять разбиение, и т. д. В крайнем случае, процесс раз%
    биений достигнет корня. И это единственный способ увеличить высоту Б%дерева.
    Странная манера расти у Б%деревьев: вверх от листьев к корню.
    Разработаем теперь подробную программу на основе этих набросков. Уже ясно, что самой удобной будет рекурсивная формулировка, так как процесс раз%
    биения может распространяться в обратном направлении по пути поиска. Поэто%
    му по общей структуре программа получится похожей на вставку в сбалансиро%
    ванное дерево, хотя в деталях будут отличия. Прежде всего нужно определить структуру страниц. Выберем представление элементов с помощью массива:
    TYPE Page = POINTER TO PageDescriptor;
    Item = RECORD key: INTEGER;
    p: Page;
    count: INTEGER (*  *)
    END;
    PageDescriptor = RECORD m: INTEGER; (* 0 .. 2n *)
    p0: Page;
    e: ARRAY 2*n OF Item
    END
    Б<деревья (B

    Динамические структуры данных
    232
    Поле count представляет любую информацию, которая может быть связана с элементом, но оно никак не участвует собственно в поиске. Заметим, что каждая страница предоставляет место для
    2n элементов. Поле m
    указывает реальное чис%
    ло элементов на данной странице. Так как m

    n
    (кроме корневой страницы), то гарантируется, что память будет использована по меньшей мере на 50%.
    Алгоритм поиска и вставки в Б%дерево сформулирован ниже в виде процедуры search
    . Его основная структура бесхитростна и подобна поиску в сбалансирован%
    ном двоичном дереве, за исключением того факта, что выбор ветви не ограничен двумя вариантами. Вместо этого поиск внутри страницы реализован как поиск делением пополам в массиве e.
    Алгоритм вставки для ясности сформулирован как отдельная процедура. Она вызывается, когда поиск показал, что нужно передать элемент вверх по дереву
    (в направлении корня). Это обстоятельство указывается булевским параметром%
    переменной h
    ; его использование аналогично случаю алгоритма вставки в сбалан%
    сированное дерево, где h
    сообщал, что поддерево выросло. Если h
    истинно, то вто%
    рой параметр%переменная u
    содержит элемент, передаваемый наверх. Заметим,
    что вставки начинаются в виртуальных страницах, а именно в так называемых дополнительных узлах (см. рис. 4.19); при этом новый элемент сразу передается через параметр u
    вверх на концевую страницу для реальной вставки. Вот набро%
    сок этой схемы:
    PROCEDURE search (x: INTEGER; a: Page; VAR h: BOOLEAN; VAR u: Item);
    BEGIN
    IF a = NIL THEN (*x     ,  *)
     x    u,   h  TRUE,  ,
        u          
    ELSE
            x    a.e;
    IF
     THEN
        
    ELSE
    search(x, descendant, h, u);
    IF h THEN (*         *)
    IF
          a^ < 2n THEN
      u   a^    h  FALSE
    ELSE
                    
    END
    END
    END
    END
    END search
    Если h
    равен
    TRUE
    после вызова процедуры search в главной программе, зна%
    чит, требуется разбить корневую страницу. Так как корневая страница играет осо%
    бую роль, то это действие следует запрограммировать отдельно. Оно состоит про%
    сто в размещении новой корневой страницы и вставке единственного элемента,

    233
    задаваемого параметром u
    . В результате новая корневая страница содержит единственный элемент. Полный текст программы приведен ниже, а рис. 4.45 по%
    казывает, как она строит Б%дерево при вставке ключей из следующей последова%
    тельности:
    20; 40 10 30 15; 35 7 26 18 22; 5; 42 13 46 27 8 32; 38 24 45 25;
    Точки с запятой отмечают моменты, когда сделаны «снимки», – после каждого размещения страниц. Вставка последнего ключа вызывает два разбиения и разме%
    щение трех новых страниц.
    Рис. 4.45. Рост Б:дерева порядка 2
    Поскольку каждый вызов процедуры поиска подразумевает одно копирование страницы в оперативную память, то понадобится не более k = log n
    (N)
    рекурсив%
    ных вызовов для дерева из
    N
    элементов. Поэтому нужно иметь возможность раз%
    местить k
    страниц в оперативной памяти. Это первое ограничение на размер стра%
    ницы
    2n
    . На самом деле нужно размещать больше, чем k
    страниц, так как вставка может вызвать разбиение. Отсюда следует, что корневую страницу лучше посто%
    Б<деревья (B

    Динамические структуры данных
    234
    янно держать в оперативной памяти, так как каждый запрос обязательно прохо%
    дит через корневую страницу.
    Другое достоинство организации данных с помощью Б%деревьев – удобство и экономичность чисто последовательного обновления всей базы данных. Каждая страница загружается в оперативную память в точности один раз.
    Операция удаления элементов из Б%дерева по идее довольно проста, но в дета%
    лях возникают усложнения. Следует различать две разные ситуации:
    1. Удаляемый элемент находится на концевой странице; здесь алгоритм уда%
    ления прост и понятен.
    2. Элемент находится на странице, не являющейся концевой; его нужно заме%
    нить одним из двух соседних в лексикографическом смысле элементов, ко%
    торые оказываются на концевых страницах и могут быть легко удалены.
    В случае 2 нахождение соседнего ключа аналогично соответствующему алго%
    ритму при удалении из двоичного дерева. Мы спускаемся по крайним правым указателям вниз до концевой страницы
    P
    , заменяем удаляемый элемент крайним правым элементом на
    P
    и затем уменьшаем размер страницы
    P
    на
    1
    . В любом слу%
    чае за уменьшением размера должна следовать проверка числа элементов m
    на уменьшившейся странице, так как m
    < n
    нарушает характеристическое свойство
    Б%деревьев. Здесь нужно предпринять дополнительные действия; это условие не
    достаточной заполненности (underflow) указывается булевским параметром h
    Единственный выход – позаимствовать элемент с одной из соседних страниц,
    скажем
    Q
    . Поскольку это требует считывания страницы
    Q
    в оперативную память, –
    что относительно дорого, – есть соблазн извлечь максимальную пользу из этой нежелательной ситуации и позаимствовать за один раз больше одного элемента.
    Обычная стратегия – распределить элементы на страницах
    P
    и
    Q
    поровну на обеих страницах. Это называется балансировкой страниц (page balancing).
    Разумеется, может случиться, что элементов для заимствования не осталось,
    поскольку страница
    Q
    достигла минимального размера n
    . В этом случае общее число элементов на страницах
    P
    и
    Q
    равно
    2n–1
    , и можно объединить обе страни%
    цы в одну, добавив средний элемент со страницы%предка для
    P
    и
    Q
    , а затем пол%
    ностью избавиться от страницы
    Q
    . Эта операция является в точности обраще%
    нием разбиения страницы. Целиком процесс проиллюстрирован удалением ключа 22 на рис. 4.44. И здесь снова удаление среднего ключа со страницы%пред%
    ка может уменьшить размер последней ниже разрешенного предела n
    , тем са%
    мым требуя выполнения специальных действий (балансировки или слияния) на следующем уровне. В крайнем случае, слияние страниц может распространять%
    ся вверх до самого корня. Если корень уменьшается до размера 0, следует уда%
    лить сам корень, что вызывает уменьшение высоты Б%дерева. На самом деле это единственная ситуация, когда может уменьшиться высота Б%дерева. Рисунок
    4.46 показывает постепенное уменьшение Б%дерева с рис. 4.45 при последова%
    тельном удалении ключей
    25 45 24; 38 32; 8 27 46 13 42; 5 22 18 26; 7 35 15;

    235
    Здесь, как и раньше, точки с запятой отмечают места, где делаются «снимки»,
    то есть там, где удаляются страницы. Следует особо подчеркнуть сходство струк%
    туры алгоритма удаления с соответствующей процедурой для сбалансированного дерева.
    Рис. 4.46. Удаление элементов из Б:дерева порядка 2
    TYPE Page = POINTER TO PageRec;
    (* ADruS471_Btrees *)
    Entry = RECORD
    key: INTEGER; p: Page
    END;
    PageRec = RECORD
    m: INTEGER; (*       *)
    p0: Page;
    e: ARRAY 2*N OF Entry
    END;
    VAR root: Page; W: Texts.Writer;
    Б<деревья (B

    Динамические структуры данных
    236
    PROCEDURE search (x: INTEGER; VAR p: Page; VAR k: INTEGER);
    VAR i, L, R: INTEGER; found: BOOLEAN; a: Page;
    BEGIN
    a := root; found := FALSE;
    WHILE (a # NIL) &

    found DO
    L := 0; R := a.m; (*      *)
    WHILE L < R DO
    i := (L+R) DIV 2;
    IF x <= a.e[i].key THEN R := i ELSE L := i+1 END
    END;
    IF (R < a.m) & (a.e[R].key = x) THEN found := TRUE
    ELSIF R = 0 THEN a := a.p0
    ELSE a := a.e[R–1].p
    END
    END;
    p := a; k := R
    END search;
    PROCEDURE insert (x: INTEGER; a: Page; VAR h: BOOLEAN; VAR v: Entry);
    (*a # NIL. ’   x  ‡–      a;
    (*
             x.
    (*
       €        ,  # v.
    (*
    h := "       "*)
    VAR i, L, R: INTEGER;
    b: Page; u: Entry;
    BEGIN
    (* h *)
    IF a = NIL THEN
    v.key := x; v.p := NIL; h := TRUE
    ELSE
    L := 0; R := a.m; (*      *)
    WHILE L < R DO
    i := (L+R) DIV 2;
    IF x <= a.e[i].key THEN R := i ELSE L := i+1 END
    END;
    IF (R < a.m) & (a.e[R].key = x) THEN (* v,    #*)
    ELSE (*    *)
    IF R = 0 THEN b := a.p0 ELSE b := a.e[R–1].p END;
    insert(x, b, h, u);
    IF h THEN (*  u    a.e[R]*)
    IF a.m < 2*N THEN
    h := FALSE;
    FOR i := a.m TO R+1 BY –1 DO a.e[i] := a.e[i–1] END;
    a.e[R] := u; INC(a.m)
    ELSE (*    ;   a a, b
                v*)
    NEW(b);
    IF R < N THEN (*       a*)

    237
    v := a.e[N–1];
    FOR i := N–1 TO R+1 BY –1 DO a.e[i] := a.e[i–1] END;
    a.e[R] := u;
    FOR i := 0 TO N–1 DO b.e[i] := a.e[i+N] END
    ELSE (*       b*)
    DEC(R, N);
    IF R = 0 THEN
    v := u
    ELSE
    v := a.e[N];
    FOR i := 0 TO R–2 DO b.e[i] := a.e[i+N+1] END;
    b.e[R–1] := u
    END;
    FOR i := R TO N–1 DO b.e[i] := a.e[i+N] END
    END;
    a.m := N; b.m := N; b.p0 := v.p; v.p := b
    END
    END
    END
    END
    END insert;
    PROCEDURE underflow (c, a: Page; s: INTEGER; VAR h: BOOLEAN);
    (*a =   , # v      (underflow),
    (*
    c =   – ,
    (*
    s =     #   c*)
    VAR b: Page; i, k: INTEGER;
    BEGIN
    (*h & (a.m = N–1) & (c.e[s–1].p = a) *)
    IF s < c.m THEN (*b :=      a*)
    b := c.e[s].p;
    k := (b.m–N+1) DIV 2; (*k =         b*)
    a.e[N–1] := c.e[s]; a.e[N–1].p := b.p0;
    IF k > 0 THEN (*          k–1     b a*)
    FOR i := 0 TO k–2 DO a.e[i+N] := b.e[i] END;
    c.e[s] := b.e[k–1]; b.p0 := c.e[s].p;
    c.e[s].p := b; DEC(b.m, k);
    FOR i := 0 TO b.m–1 DO b.e[i] := b.e[i+k] END;
    a.m := N–1+k; h := FALSE
    ELSE (*   a  b, b v € *)
    FOR i := 0 TO N–1 DO a.e[i+N] := b.e[i] END;
    DEC(c.m);
    FOR i := s TO c.m–1 DO c.e[i] := c.e[i+1] END;
    a.m := 2*N; h := c.m < N
    END
    ELSE (*b :=      a*)
    DEC(s);
    IF s = 0 THEN b := c.p0 ELSE b := c.e[s–1].p END;
    k := (b.m–N+1) DIV 2; (*k =         b*)
    Б<деревья (B

    Динамические структуры данных
    238
    IF k > 0 THEN
    FOR i := N–2 TO 0 BY –1 DO a.e[i+k] := a.e[i] END;
    a.e[k–1] := c.e[s]; a.e[k–1].p := a.p0;
    (*    k–1     b a,  c*) DEC(b.m, k);
    FOR i := k–2 TO 0 BY –1 DO a.e[i] := b.e[i+b.m+1] END;
    c.e[s] := b.e[b.m]; a.p0 := c.e[s].p;
    c.e[s].p := a; a.m := N–1+k; h := FALSE
    ELSE (*   a  b, a v € *)
    c.e[s].p := a.p0; b.e[N] := c.e[s];
    FOR i := 0 TO N–2 DO b.e[i+N+1] := a.e[i] END;
    b.m := 2*N; DEC(c.m); h := c.m < N
    END
    END
    END underflow;
    PROCEDURE delete (x: INTEGER; a: Page; VAR h: BOOLEAN);
    (*      x  ‡–   a;
    (*
         v     ,
    (*
                ;
    (*
    h := "   v     "*)
    VAR i, L, R: INTEGER; q: Page;
    PROCEDURE del (p: Page; VAR h: BOOLEAN);
    VAR k: INTEGER; q: Page; (*#   a, R*)
    BEGIN k := p.m–1; q := p.e[k].p;
    IF q # NIL THEN del(q, h);
    IF h THEN underflow(p, q, p.m, h) END
    ELSE p.e[k].p := a.e[R].p; a.e[R] := p.e[k];
    DEC(p.m); h := p.m < N
    END
    END del;
    BEGIN
    IF a # NIL THEN
    L := 0; R := a.m; (*      *)
    WHILE L < R DO
    i := (L+R) DIV 2;
    IF x <= a.e[i].key THEN R := i ELSE L := i+1 END
    END;
    IF R = 0 THEN q := a.p0 ELSE q := a.e[R–1].p END;
    IF (R < a.m) & (a.e[R].key = x) THEN (* v*)
    IF q = NIL THEN (*a —       *)
    DEC(a.m); h := a.m < N;
    FOR i := R TO a.m–1 DO a.e[i] := a.e[i+1] END
    ELSE
    del(q, h);
    IF h THEN underflow(a, q, R, h) END
    END
    ELSE

    239
    delete(x, q, h);
    IF h THEN underflow(a, q, R, h) END
    END
    END
    END delete;
    PROCEDURE ShowTree (VAR W: Texts.Writer; p: Page; level: INTEGER);
    VAR i: INTEGER;
    BEGIN
    IF p # NIL THEN
    FOR i := 1 TO level DO Texts.Write(W, 9X) END;
    FOR i := 0 TO p.m–1 DO Texts.WriteInt(W, p.e[i].key, 4) END;
    Texts.WriteLn(W);
    IF p.m > 0 THEN ShowTree(W, p.p0, level+1) END;
    FOR i := 0 TO p.m–1 DO ShowTree(W, p.e[i].p, level+1) END
    END
    END ShowTree;
    Эффективность Б%деревьев изучалась весьма подробно, результаты можно найти в упомянутой статье Бэйера и Маккрейта. В частности, там обсуждается вопрос оптимального размера страницы, который сильно зависит от характерис%
    тик используемой вычислительной системы и памяти.
    Вариации на тему Б%деревьев обсуждаются у Кнута ([2.7], с. 521–525 перево%
    да). Заслуживает внимания наблюдение, что разбиение страницы следует откла%
    дывать, как следует откладывать и слияние страниц, и сначала пытаться сбалан%
    сировать соседние страницы. В остальном похоже, что выгода от предлагавшихся улучшений незначительна. Весьма полный обзор Б%деревьев можно найти в [4.8].
    1   ...   14   15   16   17   18   19   20   21   22


    написать администратору сайта