Хардкорная схема красно-черного дерева и рукописная реализация

Java задняя часть
Хардкорная схема красно-черного дерева и рукописная реализация

Плагины IDEA, обычно используемые программистами:GitHub.com/silently952…

Публичный аккаунт WeChat: бета изучает Java

предисловие

В предыдущей статье мы использовали бинарное дерево в качестве реализации Map и, наконец, проанализировали временную сложность этой версии и наихудшего случая; в этой статье мы будем использовать красно-черное дерево для реализации Map и улучшения бинарного древовидная версия в предыдущей статье.Недостатки;определение интерфейса карты и публичные методы, которые были реализованы, не будут повторяться, такие как метод поиска (get) бинарного дерева;если вы не понимаете, пожалуйста обратитесь к предыдущей статье "Реализация карты на основе двоичного дерева"

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

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

Нужно еще понять и освоить красно-черное дерево.HashMap в Java преобразует связанный список в красно-черное дерево, когда количество узлов в связанном списке превысит 8. Нижний слой TreeMap также красно-черный. черное дерево;

По сравнению с бинарным деревом, которое мы обсуждали в предыдущей статье, красно-черное дерево почти идеально сбалансировано и может гарантировать, что время выполнения операции является логарифмическим.

Прежде чем изучать красно-черное дерево, давайте посмотрим на свойства красно-черного дерева.Для каждого свойства нам нужно задать вопрос Почему, и выучить красно-черное дерево с вопросами, если мы поймем эти вопросы, то понимаем Суть красно-черного дерева, конечно, далека от реализации.

  • Свойства 1. Узлы красные или черные. (почему: почему узлы различают цвета и какова роль красного узла?)
  • Свойство 2. Корневой узел черный. (почему: почему корневой узел должен быть черным)
  • Свойство 3. Все листья черные. (Зачем)
  • Свойство 4. На всех путях от каждого листа к корню не может быть двух последовательных красных узлов. (Зачем)
  • Природа 5. Все пути каждого листа из любого узла включают одинаковое количество черных узлов. (Зачем)
  • Свойство 6. Каждый вновь вставленный узел должен быть красным (почему)

Сбалансированное дерево поиска

Красно-черное дерево является приблизительно сбалансированным бинарным деревом.Теоретическая модель, соответствующая красно-черному дереву, может быть деревом 2-3 или деревом 2-3-4, поэтому перед изучением красно-черного дерева нам нужно понять дерево 2-3 и дерево 2-3-4;

Отношение между красно-черным деревом и деревом 2-3 и деревом 2-3-4 похоже на отношение между интерфейсом и классом реализации; дерево 2-3 и дерево 2-3-4 являются интерфейсами, а красно-черное дерево основано на реализации интерфейса.

Дерево 2-3 и дерево 2-3-4 являются частными случаями дерева B.

2-3 дерева

Чтобы гарантировать сбалансированное дерево, позволяя узлам в дереве хранит множество ключей, может присутствовать в уземе 2-3 дерева существует двумя ключевыми ссылками, но также позволяет тому же узлу содержать до двух ключевых трех ссылок;

Дерево 2-3 ниже:

найти

Процесс поиска дерева 2-3 аналогичен процессу поиска бинарного дерева.Ключ поиска сравнивается с ключом в узле.Если встречается узел, равный ключу поиска, поиск завершается, в противном случае он продолжается. для поиска в соответствующем поддереве Если встречается пустая ссылка Указывает на промах поиска.

вставлять

  • Вставить в узел с помощью одного ключевого ключа: текущий узел имеет только один ключ, добавить новый ключ непосредственно в текущий узел

  • Вставить в узел двойного ключа: сначала вставить новый ключ в текущий узел, если текущий узел больше двух ключей

  • Вставьте в узел ключа двойной клавиши, а родительский узел также является двойной кнопкой

Демонстрируя приведенные выше три случая, мы обнаружили, что рост 2-3 дерева и стандартного бинарного дерева различен: рост бинарного дерева происходит сверху вниз, а рост 2-3 дерева — снизу вверх. .

В двоичном дереве из предыдущей статьи мы обнаружили, что в худшем случае вставленные узлы упорядочены, что приводит к вырождению двоичного дерева в связанный список и снижению производительности.Мы используем вставку 2-3 порядка дерева, чтобы увидеть как, например, вставить 1, 2 по порядку, 3, 4, 5, 6, 7

Из этого мы видим, что в худшем случае дерево 2-3 по-прежнему идеально сбалансировано и имеет лучшую производительность. Но это теория, и до реальной реализации еще далеко

Левое красно-черное дерево на основе 2-3 дерева

После понимания теоретической модели красно-черного дерева 2-3 дерева мы можем реализовать наше красно-черное дерево на основе дерева 2-3.

Поскольку в дереве 2-3 есть узлы с двойной связью, поскольку нам нужно представить узлы с двойной связью в двоичном дереве, нам нужно добавить цвет атрибута цвета к узлу.Если узел красный, это означает, что текущий узел и родитель Узлы вместе образуют узел с двойной связью в дереве 2-3.

Внесите некоторые изменения в узлы бинарного дерева из предыдущей статьи, код выглядит следующим образом:

class Node implements TreeNode {
    public static final boolean RED = true;
    public static final boolean BLACK = false;
    public K key;
    public V value;
    public Node left;
    public Node right;
    public boolean color; //RED 或者 BLACK
    public int size = 1;  //当前节点下节点的总个数

    public Node(K key, V value, boolean color) {
        this.key = key;
        this.value = value;
        this.color = color;
    }

    @Override
    public Node getLeft() {
        return this.left;
    }

    @Override
    public Node getRight() {
        return this.right;
    }

    @Override
    public Color getColor() {
        return color ? Color.RED : Color.BLACK;
    }
}

Поскольку красно-черное дерево само по себе также является бинарным деревом, метод поиска по бинарному дереву, реализованный в предыдущей статье, может быть непосредственно применен к красно-черному дереву без каких-либо модификаций.

В этой статье мы реализовали алгоритм ссылки красно-черного дерева 2-3 дерева 4,И мы оговариваем, что красный узел может появиться только в левом узле, то есть красно-черном дереве с наклоном влево.(Почему оговаривается, что красный узел может появляться только на левом узле? На самом деле возможна и правая сторона, только левый узел может существовать, потому что это может уменьшить возможные ситуации, а код, необходимый для реализации, относительно маленький)

Объяснение свойств красно-черных деревьев

Красно-черное дерево достигается с помощью 2-3 деревьев.На основе 2-3 деревьев, чтобы увидеть природу красно-черного дерева, это не условность, не нужно заставлять память.

  • Свойства 1. Узлы красные или черные.(почему: почему узлы различают цвета и какова роль красного узла?)

Как мы объяснили выше, основная причина для выделения цвета состоит в том, чтобы представить узел двойной связи дерева 2-3 в двоичном дереве.Если это красный узел, это означает, что текущий узел и родительский узел вместе образуют 2-3. Двойные связи чисел, черные узлы представляют собой нормальные узлы в бинарном дереве.

  • Свойство 2. Корневой узел черный.(почему: почему корневой узел должен быть черным)

Он по-прежнему основан на роли красного узла.Сам корневой узел не имеет родительского узла и не может образовывать двойную связь 2-3 дерева, поэтому он не может быть красным узлом.

  • Свойство 3. Все листья черные.(Зачем)

Упомянутые здесь листья на самом деле являются пустыми ссылками, а пустые ссылки на приведенном выше рисунке не показаны.

  • Свойство 4. На всех путях от каждого листа к корню не может быть двух последовательных красных узлов.(why)

Это свойство все еще понимается исходя из роли красного узла.Если есть два последовательных красных узла, то с родительским узлом формируется узел с 3-ключом, но это не допускается в левостороннем красно-черном дерево реализовано деревом 2-3. . (В красно-черном дереве на основе дерева 2-3-4 разрешено 3 ключа, но только по одному красному узлу с левой и правой сторон, и оно не может быть непрерывным. Позже в части расширения будет объяснение)

  • Свойство 5. Все пути от любого узла к каждому его листу содержат одинаковое количество черных узлов.(why)

Это свойство можно понять на основе теоретической модели дерева 2-3, поскольку красный узел представляет тот же уровень, что и родительский узел, поэтому в красно-черном дереве только черный узел будет вносить вклад в высоту дерева, поэтому от любого узла к его Все пути к каждому листу содержат одинаковое количество черных узлов

  • Свойство 6. Каждый недавно вставленный узел должен быть красным(Зачем)

Это свойство отсутствует в энциклопедии Baidu, но я видел его на некоторых зарубежных сайтах, лично я считаю, что это полезно для понимания красно-черных деревьев, поэтому я добавил его здесь, мы продемонстрировали процесс вставки ключей в 2-3 деревья выше. ,Сначала вставьте значение ключа в узел, а затем судить, нужно ли расщеплять, потому что двойная связь или 3-связь, которая построена на текущем узле для формирования дерева 2-3, предпочтительно вставляется, а в красно-черное дерево - только с помощью красного узла и родительский узел, чтобы сформировать двойную связь дерева 2-3 или 3 ключей, поэтому каждый вновь вставленный узел должен быть красным.

Дерево 2-3 представлено левым красно-черным деревом.

  • Представление одноключевого узла в дереве 2-3 в красно-черном дереве

  • 2-3 Узел дерева представляет двойную связь в красно-черном дереве, как и левое красно-черное дерево, мы можем появиться только на левой стороне красного узла

Может быть не интуитивно смотреть только на изменения узлов Мы можем видеть, как дерево 2-3 может быть представлено как красно-черное дерево с наклоном влево.

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

Определить цвет узла

Мне нужно определить метод, чтобы определить, к какому цвету принадлежит узел, и вернуть true, если он красный, иначе вернуть false

protected boolean isRed(Node node) {
    if (Objects.isNull(node)) {
        return Node.BLACK;
    }
    return node.color;
}

вращать

При внедрении вставки или удаления красно-черного дерева могут быть красные узлы справа или два последовательных красных узла. В этих случаях нам необходимо завершить ремонт через операцию вращения.

Поскольку ссылка родительского узла должна быть изменена после завершения операции вращения, определяемый нами метод вращения должен возвращать узел после вращения, чтобы сбросить ссылку родительского узла.

  • Левша

Левосторонний код реализуется следующим образом:

protected Node rotateLeft(Node h) {
    Node x = h.right;
    h.right = x.left;
    x.left = h;
    x.color = h.color;
    h.color = Node.RED;

    size(h); //计算子树节点的个数
    size(x);

    return x;
}

Переназначьте связи левого и правого поддеревьев двух узлов и измените цвет узлов. Второй — подсчитать количество узлов, содержащихся в каждом поддереве. Метод расчета аналогичен реализации размера бинарного дерева в предыдущей статье. Здесь описание повторяться не будет. См. «Реализация карты на основе бинарных Дерево"

  • правша

Правый код реализуется следующим образом:

protected Node rotateRight(Node h) {
    Node x = h.left;
    h.left = x.right;
    x.right = h;
    x.color = h.color;
    h.color = Node.RED;

    size(h);
    size(x);

    return x;
}

Обесцвечивать

В дереве 2-3, когда значение ключа в узле достигает 3, его нужно разделить, и один из узлов всплывет, как должна быть представлена ​​эта операция в красно-черном дереве?

Для достижения разбиения узлов в красно-черном дереве фактически используется изменение цвета узла; после поворота красно-черного дерева влево и вправо родительский узел в конечном итоге станет черным, а левый и правый дочерние узлы будут красный (вы можете увидеть подробности в операции вставки позже. Процесс преобразования), это состояние соответствует ситуации трехключевого узла в дереве 2-3. В это время операция разделения должна изменить цвет левый и правый дочерние узлы черным цветом, а родительский узел красным.

Код реализован следующим образом:

/转换颜色,对应了23树中的节点分裂
protected void upSplit(Node node) {
    if (Objects.isNull(node)) {
        return;
    }
    node.left.color = Node.BLACK;
    node.right.color = Node.BLACK;
    node.color = Node.RED;
}

вставлять

Вставить в один ключевой узел
  1. Если вставленный ключ меньше, чем значение ключа текущего узла, то непосредственно добавьте красный левый узел

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

Вставьте в узел двойной связи
  1. Существует три случая вставки нового ключа в узел с двумя ключами. Сначала рассмотрим самый простой случай. Значение вставленного ключа является наибольшим. После вставки необходимо изменить только цвет. Процесс изменения показан на рис. рисунок ниже.

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

  1. В третьем случае вставленное значение ключа находится между двумя исходными ключами, его нужно повернуть влево, затем повернуть вправо и, наконец, изменить цвет.

По анализу вышеперечисленных ситуаций можно резюмировать единый закон изменений:

  • Если правый дочерний узел красный, а левый дочерний узел — черное дерево, то левосторонний
  • Если левый потомок красный и его левый потомок красный, то процесс правовращения
  • Если левый и правый дочерние узлы красные, выполните преобразование цвета

График меняется следующим образом:

После вышеприведенного анализа теперь мы можем использовать код для реализации операции вставки красно-черного дерева.

@Override
public void put(K key, V value) {
    if (Objects.isNull(key)) {
        throw new IllegalArgumentException("the key can't null");
    }
    root = put(root, key, value);
    root.color = Node.BLACK; 
}

private Node put(Node node, K key, V value) {
    if (Objects.isNull(node)) {
        return new Node(key, value, Node.RED);
    }
    int compare = key.compareTo(node.key);
    if (compare > 0) {
        node.right = put(node.right, key, value);
    } else if (compare < 0) {
        node.left = put(node.left, key, value);
    } else {
        node.value = value;
    }

    if (isRed(node.right) && !isRed(node.left)) {
        node = rotateLeft(node);
    }
    if (isRed(node.left) && isRed(node.left.left)) {
        node = rotateRight(node);
    }
    if (isRed(node.left) && isRed(node.right)) {
        upSplit(node);
    }

    size(node);
    return node;
}

Из-за того, что корневой узел должен быть черным, операция изменения цвета не позволяет корневому узлу стать красным, поэтому мы равномерно устанавливаем корневой узел черным после операции вставки;

Первая половина операции вставки красно-черного дерева аналогична операции вставки бинарного дерева, реализованной в предыдущей статье.Единственное отличие состоит в последних трех операциях if.Эти три операции являются кодовой реализацией унифицированного изменить правило, описанное выше.

Первая обработка суждения Если правый узел красный, а левый черный, то выполняется левое вращение.

Второй процесс определения, если левый узел красный и его левый узел красный, то процесс правостороннего

Третий, если считает, что если левый и правый дочерние узлы оба красные, то измените цвет

Удалить

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

Прежде всего, нам нужно вернуться к теоретической модели дерева 2-3.Если удаляемый нами узел в данный момент является узлом с двойной связью, то мы можем удалить его напрямую, при этом высота дерева не изменится и структура не изменится, так что красное и черное Ключ к операции удаления дерева состоит в том, чтобы гарантировать, что удаляемый узел является узлом с двойным ключом.

При выполнении операции удаления мы также реализуем операцию изменения цвета.Операция изменения цвета здесь противоположна операции изменения цвета вставки.Родительский узел становится черным, а два дочерних узла становятся красными.

protected void flipColors(Node h) {
    h.color = !h.color;
    h.left.color = !h.left.color;
    h.right.color = !h.right.color;
}

Прежде чем формально реализовать операцию удаления, давайте обсудим случай удаления минимального и максимального значений красно-черного дерева.Реализованная в конце операция удаления также будет использовать удаление минимального значения и удаление максимального значения .

Удалить минимум

Чтобы удалить минимальное значение в двоичном дереве, нужно выполнить поиск по левому поддереву дерева до тех пор, пока левое поддерево узла не станет нулевым, а затем удалить узел

Минимальное значение удаления красно-черного дерева аналогично, но нам нужно убедиться, что удаляемый узел является узлом с двойной связью, поэтому при рекурсии к каждому узлу нам нужно сохранить текущий узел как узел с двойной связью. узел, то минимальное значение, найденное в конце. Оно должно быть в узле с двойным ключом (поскольку родительский узел, который был сохранен во время рекурсии, является узлом с двойным ключом).

Так что, если текущий рекурсивный узел гарантированно будет узлом с двойным ключом? Здесь будет 3 случая:

  • Левый дочерний узел текущего узла является узлом с двойным ключом, удалите его напрямую

  • Левый дочерний узел текущего узла является узлом с одним ключом, но его брат является узлом с двумя ключами, затем, вращая и перемещая узел к левому дочернему узлу, чтобы сформировать узел с двумя ключами, затем выполните операцию удаления.

  • После левого дочернего узла и правого дочернего узла текущего узла идет одинарная связь, затем вместе тройная связь и родительский узел по цвету, а затем удаление

Выше приведены все ситуации, с которыми столкнется красно-черное дерево при удалении минимального значения.Для последних двух ситуаций, для простоты реализации кода, рассмотрим объединение этих двух ситуаций;

Сначала инициализируйте корневой узел красным, затем измените цвет, а затем оцените, является ли узел node.right.left красным, и если да, выполните операцию поворота.

Реализация кода для удаления минимального значения выглядит следующим образом:

private Node moveToRedLeft(Node h) {
    flipColors(h);
    if (isRed(h.right.left)) {
        h.right = rotateRight(h.right);
        h = rotateLeft(h);
        flipColors(h);
    }
    return h;
}

@Override
public void deleteMin() {
    if (isEmpty()) {
        throw new NoSuchElementException("BST underflow");
    }

    if (!isRed(root.left) && !isRed(root.right)) {
        root.color = Node.RED;
    }

    root = deleteMin(root);
    if (!isEmpty()) {
        root.color = Node.BLACK;
    }
}

private Node deleteMin(Node h) {
    if (h.left == null) {
        return null;
    }

    if (!isRed(h.left) && !isRed(h.left.left)) {
        h = moveToRedLeft(h);
    }

    h.left = deleteMin(h.left);
    return balance(h);
}

private Node balance(Node h) {
    if (isRed(h.right) && !isRed(h.left)) {
        h = rotateLeft(h);
    }
    if (isRed(h.left) && isRed(h.left.left)) {
        h = rotateRight(h);
    }
    if (isRed(h.left) && isRed(h.right)) {
        flipColors(h);
    }

    h.size = size(h.left) + size(h.right) + 1;
    return h;
}

После удаления минимального значения нам нужно повторно восстановить красно-черное дерево, потому что наши предыдущие операции могут привести к существованию 3-ключевых узлов.После удаления нам нужно повторно разложить 3-узлы построения, баланс в приведенном выше коде нужно заново восстановить красно-черное дерево.

удалить макс.

Похожие идеи удаляйте максимум и минимум удаляйте идеи, подробно здесь не описанные, прямо на карте

Удаление максимального значения требует заимствования узла из левого узла, код реализован следующим образом:

@Override
public void deleteMax() {
    if (isEmpty()) {
        throw new NoSuchElementException("BST underflow");
    }

    if (!isRed(root.left) && !isRed(root.right)) {
        root.color = Node.RED;
    }

    root = deleteMax(root);
    if (!isEmpty()) {
        root.color = Node.BLACK;
    }

}

private Node deleteMax(Node node) {
    if (isRed(node.left)) { //此处与删除最小值不同,如果左边是红色,那么先借一个节点到右边来
        node = rotateRight(node);
    }
    if (Objects.isNull(node.right)) {
        return null;
    }
    if (!isRed(node.right) && !isRed(node.right.left)) {
        node = moveToRedRight(node);
    }
    node.right = deleteMax(node.right);
    return balance(node);
}

private Node moveToRedRight(Node node) {
    flipColors(node);
    if (isRed(node.left.left)) {
        node = rotateRight(node);
        flipColors(node);
    }
    return node;
}

удалить любой узел

Выполнение того же преобразования, что и удаление минимального значения на пути поиска, может гарантировать, что любой текущий узел не будет узлом с двойным ключом в процессе поиска;

Если значение ключа поиска находится на левом узле, то выполнить изменение аналогично удалению минимального значения, заимствуя узел справа;

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

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

Код реализован следующим образом:

@Override
public void delete(K key) {
    if (isEmpty()) {
        throw new NoSuchElementException("BST underflow");
    }

    if (!isRed(root.left) && !isRed(root.right)) {
        root.color = Node.RED;
    }

    root = delete(root, key);
    if (!isEmpty()) {
        root.color = Node.BLACK;
    }
}


private Node delete(Node node, K key) {
    int compare = key.compareTo(node.key);
    if (compare < 0) {//左子树中查找
        if (!isRed(node.left) && !isRed(node.left.left)) {
            node = moveToRedLeft(node);
        }
        node.left = delete(node.left, key);
    } else if (compare > 0) { //右子树中查找
        if (isRed(node.left)) {
            node = rotateRight(node);
        }
        if (!isRed(node.right) && !isRed(node.right.left)) {
            node = moveToRedRight(node);
        }
        node.right = delete(node.right, key);
    } else {
        if (Objects.isNull(node.left) && Objects.isNull(node.right)) {
            return null; //叶子节点直接结束
        }

        if (Objects.nonNull(node.left)) { //左子树不为空
            Node max = max(node.left);
            node.key = max.key;
            node.value = max.value;
            node.left = deleteMax(node.left);
        } else { //右子树不为空
            Node min = min(node.right);
            node.key = min.key;
            node.value = min.value;
            node.right = deleteMin(node.right);
        }
    }
    return balance(node);
}

Нарисуйте красно-черное дерево, чтобы проверить реализацию

Выше мы реализовали основной код красно-черного дерева, но как проверить, является ли наше красно-черное дерево настоящим красно-черным деревом, лучше всего нарисовать красно-черное дерево на основе реализованной нами версии, а затем передать свойства красно-черного дерева для проверки

Так как как нарисовать красно-черное дерево не тема этой статьи, я не буду выкладывать код, а друзья, кому он нужен, могут съездить на склад проверить его;

Напишите модуль для проверки карты, которую мы реализовали с красно-черным деревом, и нарисуйте красно-черное дерево, чтобы убедиться, что оно правильное.

@Test
public void testDelete() throws IOException {
    RedBlack23RedBlackTreeMap<Integer, String> map = new RedBlack23RedBlackTreeMap<>();
    map.put(8, "80");
    map.put(18, "180");
    map.put(5, "50");
    map.put(15, "150");
    map.put(17, "170");
    map.put(25, "250");
    map.put(40, "40");
    map.put(80, "80");
    map.put(30, "30");
    map.put(60, "60");
    map.put(16, "16");

    map.draw("/Users/huaan9527/Desktop/redBlack4.png"); //画出删除之前的红黑树
    map.delete(40);
    map.delete(17);
    map.delete(25);
    map.nodes().forEach(System.out::println); //根据key从小到大顺序打印出节点

    map.draw("/Users/huaan9527/Desktop/redBlack5.png"); //画出删除之后的红黑树
}

Последовательно распечатайте результаты выполнения узла:

Удалить предыдущее красно-черное дерево

Удалите красное черное дерево

Суммировать

В этой статье в основном обсуждается версия красно-черного дерева, основанная на реализации дерева 2-3. Если вы поймете и освоите содержание этой статьи, вы также освоите красно-черное дерево.Ничего плохого в интервью.

Чтобы углубить понимание красно-черного дерева, вы можете реализовать красно-черное дерево на основе дерева 2-3-4, Чтобы сравнить различия версий между двумя красно-черными деревьями, вы можете обратиться к коду в моем репозитории git.


Весь исходный код этой статьи помещен в репозиторий github:GitHub.com/silently952…

Продемонстрируйте веб-сайт красно-черного дерева. Способ, которым веб-сайт реализует красно-черное дерево, отличается от того, как мы реализуем его в этой статье. Вы можете обратиться к следующему:Ууууу, в это время Джейд А. Квота из США/~Карри Лакса/виз…

Наконец (обратите внимание, не потеряйтесь)

В тексте может быть больше или меньше недостатков и ошибок.Если у вас есть предложения или мнения, вы можете их комментировать и обмениваться.

Наконец,Писать не легко, пожалуйста, не проституируй меня., я надеюсь, что мои друзья могутНравится Комментарий ПодписатьсяСанлян, потому что это все источники мотивации, которыми я могу поделиться🙏