English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Полное Java- 实现 алгоритма B-дерева

Определение

В компьютерной науке, B-дерево (на английском: B-tree) - это самоуравновешиваемое дерево, которое может поддерживать данные в порядке. Эта структура данных позволяет выполнять операции поиска данных,顺序ного доступа, вставки данных и удаления в czasie логарифма.

Почему был введен B-дерево?

Прежде всего, включая介绍的 красно-черное дерево, это хранение вводапамятиодного извнутреннее дерево поиска.

B-дерево является расширением алгоритма平衡ированного дерева, оно поддерживает сохранениена диске или в сетив символьной таблицевнешний поискЭти файлы могут быть значительно больше, чем те, которые мы учитывали раньше (их трудно хранить в памяти).

Поскольку содержимое хранится на диске, естественно, это приведет к чрезмерно частым операциям ввода-вывода I/O из-за слишком большой глубины дерева (скорость чтения и записи диска ограничена), что в свою очередь приведет к низкой эффективности поиска.

Таким образом, снижение глубины дерева естественно очень важно. Поэтому мы ввели B-дерево, многопутное lookup-дерево.

Особенности

Каждый узел в дереве может содержать до m детей (m>=2);

За исключением корневого узла и листовых узлов, каждый узел, по крайней мере, имеет [ceil(m / 2)] детей (где ceil(x) - функция, которая вычисляет целое значение не менее x);

Если корневой узел не является листовым узлом, то у него至少 есть 2 ребенка (специальный случай: корень без детей, то есть корень является листовым узлом, и у дерева всего один корневой узел);

Все листовые узлы расположены на одном уровне (нижнем уровне),Листовые узлы являются внешними узлами, сохраняющими содержимое, то есть ключ и значение.

Другие узлы являются внутренними узлами, сохраняющими индекс, то есть ключ и next.

Ключи во внутреннем узле: K[1], K[2], …, K[M-1]; и K[i] < K[i+1];

Указатели на содержательные узлы: P[1], P[2], …, P[M]; где P[1] указывает на поддерево, ключи которого меньше K[1], P[M] указывает на поддерево, ключи которого больше K[M-1], другие P[i] указывают на поддеревья, ключи которых принадлежат интервалу (K[i-1], K[i]);

Например: (M=3)

Поиск и вставка

Для удобства используется специальный sentinel ключ, который меньше всех других ключей и обозначается звёздочкой (*)

Вначале B-дерево содержит только один корневой узел, который в момент инициализации содержит только sentinel ключ.

Каждый ключ во внутреннем узле связан с узлом, и все ключи в поддереве, корнем которого является этот узел, больше или равны ключу, связанному с этим узлом, но меньше всех других ключей.

Эти соглашения значительно упрощают код.

Код

Нажмите, чтобы загрузить.

Данное кодирование внедряет sentinel ключ, но в выводе кода он удаляется.

B-дерево с sentinel ключом, выводимое кодом (сохраните изображение на локальном диске, чтобы текст был более разборчивым):

B-дерево, выводимое кодом (сохраните изображение на локальном диске, чтобы текст был более разборчивым):

public class BTree<Key extends Comparable<Key>, Value> 
{
  // максимальное количество детей в узле B-дерева = M-1
  // (должно быть четным и больше 2)
  private static final int M = 4;
  private Node root;    // корень B-дерева
  private int height;   // высота B-дерева
  private int n;      // количество пар ключ-значение в B-дереве
  // helper B-tree node data type
  private static final class Node 
  {
    private int m;               // number of children
    private Entry[] children = new Entry[M];  // the array of children
    // create a node with k children
    private Node(int k) 
    {
      m = k;
    }
  }
  // internal nodes: only use key and next
  // external nodes: only use key and value
  private static class Entry 
  {
    private Comparable key;
    private Object val;
    private Node next;   // helper field to iterate over array entries
    public Entry(Comparable key, Object val, Node next) 
    {
      this.key = key;
      this.val = val;
      this.next = next;
    }
  }
  /**
   * Initializes an empty B-tree.
   */
  public BTree() 
  {
    root = new Node(0);
  }
  /**
   * Returns true if this symbol table is empty.
   * @return {@code true} if this symbol table is empty; {@code false} otherwise
   */
  public boolean isEmpty() 
  {
    return size() == 0;
  }
  /**
   * Returns the number of key-value pairs in this symbol table.
   * @return the number of key-value pairs in this symbol table
   */
  public int size() 
  {
    return n;
  }
  /**
   * Возвращает высоту этого B-дерева (для отладки).
   *
   * @return высота этого B-дерева
   */
  public int height() 
  {
    return height;
  }
  /**
   * Возвращает значение, связанное с заданным ключом.
   *
   * @param key ключ
   * @return значение, связанное с заданным ключом, если ключ находится в таблице символов
   *     и {@code null}, если ключ не находится в таблице символов
   * @throws NullPointerException если ключ {@code key} является null
   */
  public Value get(Key key) 
  {
    if (key == null) 
    {
      throw new NullPointerException("ключ не может быть null");
    }
    return search(root, key, height);
  }
  @SuppressWarnings("unchecked")
  private Value search(Node x, Key key, int ht) 
  {
    Entry[] children = x.children;
    // внешние узлы до нижнего уровня листовых узлов,遍历
    if (ht == 0) 
    {
      for (int j = 0; j < x.m; j++)       
      {
        if (eq(key, children[j].key)) 
        {
          return (Value) children[j].val;
        }
      }
    }
    // внутренний узел рекурсивный поиск по адресу next
    else 
    {
      for (int j = 0; j < x.m; j++) 
      {
        if (j+1 == x.m || less(key, children[j+1].key))
        {
          return search(children[j].next, key, ht-1);
        }
      }
    }
    return null;
  }
  /**
   * Вставляет пару ключ-значение в таблицу символов, перезаписывая старое значение
   * с новым значением, если ключ уже находится в таблице символов.
   * Если значение {@code null}, это эффективно удаляет ключ из таблицы символов.
   *
   * @param key ключ
   * @param val значение
   * @throws NullPointerException если ключ {@code key} является null
   */
  public void put(Key key, Value val) 
  {
    if (key == null) 
    {
      throw new NullPointerException("ключ не может быть null");
    }
    Node u = insert(root, key, val, height); // правый узел, образовавшийся после разделения
    n++;
    if (u == null) 
    {
      return;
    }
    // необходимо разделить root重组root
    Node t = new Node(2);
    t.children[0] = new Entry(root.children[0].key, null, root);
    t.children[1] = new Entry(u.children[0].key, null, u);
    root = t;
    height++;
  }
  private Node insert(Node h, Key key, Value val, int ht) 
  {
    int j;
    Entry t = new Entry(key, val, null);
    // external node внешний узел, также является листом, находится на самом низу дерева, содержит значение content
    if (ht == 0) 
    {
      for (j = 0; j < h.m; j++) 
      {
        if (less(key, h.children[j].key)) 
        {
          break;
        }
      }
    }
    // internal node внутренний узел, содержащий адрес next
    else 
    {
      for (j = 0; j < h.m; j++) 
      {
        if ((j+1 == h.m) || less(key, h.children[j+1].key)) 
        {
          Node u = insert(h.children[j++].next, key, val, ht-1);
          if (u == null) 
          {
            return null;
          }
          t.key = u.children[0].key;
          t.next = u;
          break;
        }
      }
    }
    for (int i = h.m; i > j; i--)
    {
      h.children[i] = h.children[i-1];
    }
    h.children[j] = t;
    h.m++;
    if (h.m < M) 
    {
      return null;
    }
    else     
    {  // разделение узла
      return split(h);
    }
  }
  // разделите узел пополам
  private Node split(Node h) 
  {
    Node t = new Node(M/2);
    h.m = M/2;
    for (int j = 0; j < M/2; j++)
    {
      t.children[j] = h.children[M/2+j];     
    }
    return t;  
  }
  /**
   * Возвращает строковое представление этого B-дерева (для отладки).
   *
   * @return строковое представление этого B-дерева.
   */
  public String toString() 
  {
    return toString(root, height, "") + "\n";
  }
  private String toString(Node h, int ht, String indent) 
  {
    StringBuilder s = new StringBuilder();
    Entry[] children = h.children;
    if (ht == 0) 
    {
      for (int j = 0; j < h.m; j++) 
      {
        s.append(indent + children[j].key + " " + children[j].val + "\n");
      }
    }
    else 
    {
      for (int j = 0; j < h.m; j++) 
      {
        if (j > 0) 
        {
          s.append(indent + "(" + children[j].key + ")\n");
        }
        s.append(toString(children[j].next, ht-1, indent + "   "));
      }
    }
    return s.toString();
  }
  // сравнительные функции - вместо Key используйте Comparable для избежания преобразований
  private boolean less(Comparable k1, Comparable k2) 
  {
    return k1.compareTo(k2) < 0;
  }
  private boolean eq(Comparable k1, Comparable k2) 
  {
    return k1.compareTo(k2) == 0;
  }
  /**
   * Unit tests the {@code BTree} data type.
   *
   * @param args the command-line arguments
   */
  public static void main(String[] args) 
  {
    BTree<String, String> st = new BTree<String, String>();
    st.put("www.cs.princeton.edu", "128.112.136.12");
    st.put("www.cs.princeton.edu", "128.112.136.11");
    st.put("www.princeton.edu", "128.112.128.15");
    st.put("www.yale.edu", "130.132.143.21");
    st.put("www.simpsons.com", "209.052.165.60");
    st.put("www.apple.com", "17.112.152.32");
    st.put("www.amazon.com", "207.171.182.16");
    st.put("www.ebay.com", "66.135.192.87");
    st.put("www.cnn.com", "64.236.16.20");
    st.put("www.google.com", "216.239.41.99");
    st.put("www.nytimes.com", "199.239.136.200");
    st.put("www.microsoft.com", "207.126.99.140");
    st.put("www.dell.com", "143.166.224.230");
    st.put("www.slashdot.org", "66.35.250.151");
    st.put("www.espn.com", "199.181.135.201");
    st.put("www.weather.com", "63.111.66.11");
    st.put("www.yahoo.com",    "216.109.118.65");
    System.out.println("cs.princeton.edu: " + st.get("www.cs.princeton.edu"));
    System.out.println("hardvardsucks.com: " + st.get("www.harvardsucks.com"));
    System.out.println("simpsons.com:   " + st.get("www.simpsons.com"));
    System.out.println("apple.com:     " + st.get("www.apple.com"));
    System.out.println("ebay.com:     " + st.get("www.ebay.com"));
    System.out.println("dell.com:     " + st.get("www.dell.com"));
    System.out.println();
    System.out.println("size:  " + st.size());
    System.out.println("height: " + st.height());
    System.out.println(st);
    System.out.println();
  }
}

вывод:
cs.princeton.edu:  128.112.136.12
hardvardsucks.com: null
simpsons.com:      209.052.165.60
apple.com:         17.112.152.32
ebay.com:          66.135.192.87
dell.com:          143.166.224.230

size:    17
height:  2
          www.amazon.com 207.171.182.16
          www.apple.com 17.112.152.32
          www.cnn.com 64.236.16.20
     (www.cs.princeton.edu)
          www.cs.princeton.edu 128.112.136.12
          www.cs.princeton.edu 128.112.136.11
          www.dell.com 143.166.224.230
(www.ebay.com)
          www.ebay.com 66.135.192.87
          www.espn.com 199.181.135.201
          www.google.com 216.239.41.99
     (www.microsoft.com)
          www.microsoft.com 207.126.99.140
          www.nytimes.com 199.239.136.200
(www.princeton.edu)
          www.princeton.edu 128.112.128.15
          www.simpsons.com 209.052.165.60
     (www.slashdot.org)
          www.slashdot.org 66.35.250.151
          www.weather.com 63.111.66.11
     (www.yahoo.com)
          www.yahoo.com 216.109.118.65
          www.yale.edu 130.132.143.21

Вот и все, что есть в этой статье, я надеюсь, что это поможет вам в изучении, и希望大家多多支持呐喊教程。

Заявление: содержимое этой статьи взято из Интернета, авторские права принадлежат соответствующему автору. Контент предоставлен пользователями Интернета, автоматически загружен, сайт не имеет права собственности, не underwent редактирование, и не несет ответственности за соответствующие юридические последствия. Если вы обнаружите спорное содержание, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (во время отправки письма, пожалуйста, замените # на @) для жалоб и предоставьте соответствующие доказательства. При обнаружении, сайт немедленно удаляет спорное содержимое.

Основной учебник
Рекомендуем также