Я думаю, что ваш тест неправильный, хотя я думаю, что у вашего кода есть другие проблемы, которые тест не улавливает.
Прежде всего, свойство Height на самом деле не возвращает высоту, а количество узлов с хотя бы одним дочерним элементом. Если вы хотите высоту самого глубокого узла, вы должны вместо этого делать что-то вроде currentHeight = Math.Max(currentHeight, stack.Count)
на каждой итерации. Вы также можете захотеть вернуть int
вместо double
.
Количество узлов без потомков должно быть приблизительно половина из них, как вы хотите, но красно-черные деревья не идеально сбалансированы. У вас может быть правильное дерево, у одной трети узлов которого есть один дочерний элемент, у одной трети - два, а у одной трети нет ни одного: начните с идеально сбалансированного дерева со всеми черными узлами на последнем уровне и добавьте красный дочерний элемент к каждому. Это поддерживает инварианты красно-черного дерева, но у двух третей узлов будут дочерние элементы.
Точно так же, если бы вы проверяли глубину, она была бы между log(N)
и 2 log(N)
.
Возможно, вы захотите написать тесты, которые напрямую проверяют инварианты дерева. Посетите каждый узел в дереве и убедитесь, что у каждого красного узла есть черный родитель и что каждый путь к листу содержит одинаковое количество черных узлов. Если вы запускаете эти тесты после каждой вставки в вашем наборе тестов, вы можете быть уверены, что дерево всегда сбалансировано.
Что касается самого кода, ваш метод Rebalance сканирует все дерево при каждой вставке. Это означает, что вставка потребует O(N)
времени и сведет на нет преимущества использования самобалансирующегося дерева. Получение по-прежнему будет O(log N)
, но вы можете получить тот же результат, сохранив отсортированный список и вставив элементы в соответствующее место. Вам нужно только перебалансировать дерево вдоль вставляемого пути, который будет состоять только из O(log N)
узлов.
Я думаю, что некоторые из ваших преобразований неверны. Вы не проверяете цвет текущего узла перед вызовом Rule2, и это правило, по-видимому, меняет узлы на черный, не гарантируя, что другие пути в дереве имеют такое же количество черных узлов. (Возможно, я неправильно читаю; красно-черные деревья слишком сложны, чтобы делать их целиком в моей голове.)
Если вы ищете эталонную реализацию, на странице Википедии Красно-черные деревья есть реализация на C, которую можно легко перевести на C #, и SortedSet<T>
реализован с использованием красно-черного дерева, которое вы можете просмотреть с помощью Reflector.