Красно-черное дерево - Как найти родителя узла?

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

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

public class Node {
  private left;
  private right;
  private isRed;
  private parent; //I don't think this is good idea
}

Итак, мое решение состоит в том, чтобы написать метод findParent(), который использует поиск для поиска родителя. Мне интересно, есть ли другой способ найти родителя узла?

Мое решение:

образец дерева:

    50
    / \
   25  75

Если вы хотите найти родителя Node 25, вы передаете что-то вроде:

Node parent = findParent(Node25.value);

и он возвращает node50.

protected Node findParent(int val) {
        if(root == null) {
            return null;
        }
        Node current = root;
        Node parent = current;
        while(true) {
            if(current.val == val) { //found
                return parent;
            }
            if(current == null) { //not found
                return null;
            }
            parent = current;
            if(current.val > val) { //go left
                current = current.left;
            }
            else { //go right
                current = current.right; 
            }
        }
    }

person Meow    schedule 27.07.2010    source источник
comment
Я могу ошибаться, но я думаю, что обычный подход состоит в том, чтобы передать родителя в качестве параметра в стеке вызовов функции поворота.   -  person mikera    schedule 27.07.2010
comment
Вы должны посмотреть на libavl. Правильный способ — хранить предков в стеке при поиске.   -  person user172818    schedule 22.05.2012


Ответы (5)


Я думал дать переменной экземпляра узла «родительский», но именно по этой причине я не думаю, что это стоит делать

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

Тогда это компромисс между

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

Я думаю, что выбор между этими двумя вариантами несколько субъективен, но лично я бы предпочел просто отслеживать parent ссылки.

В качестве точки отсчета для вас java.util.TreeMap реализовано как красно-черное дерево, на которое ссылаются Entry узлы, содержащие left, right и parent.

person matt b    schedule 27.07.2010
comment
Не думаю, что это все так уж субъективно. Если вы вообще собираетесь пройти дистанцию ​​с деревом RB, это, вероятно, потому, что вы готовы пожертвовать некоторыми вещами в погоне за более быстрыми операциями. Если вам так не хватает памяти, почему бы просто не использовать массив? - person Mark Peters; 27.07.2010
comment
Что ж, я думаю, что есть некоторые алгоритмы, о которых можно сказать, что отказ от поддержки структуры данных стоит того, чтобы немного увеличить вычислительную сложность. Лично я не думаю, что это один из них. Я просто хотел избежать широкого ответа. - person matt b; 27.07.2010

Использование родительского указателя необязательно. Если вы откажетесь от родительского указателя, вам придется писать операции вставки/удаления с использованием рекурсии (вызовы рекурсивных методов сохраняют родительскую информацию в стеке) или писать итеративную версию, которая поддерживает свой собственный стек родителей по мере продвижения вниз по дереву.

Очень хорошее описание красно-черных деревьев можно найти здесь

http://adtinfo.org/

Сюда входят описания ряда реализаций rbtree, в том числе с родительскими указателями и без них.

Если вы хотите сэкономить место (и это справедливо), отличное описание реализации rbtree можно найти здесь.

http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx

Описанный вами метод поиска родителя узла был бы очень неэффективным, если бы он использовался реализациями вставки/удаления. Используйте указатель или используйте рекурсию.

person Francis Stephens    schedule 17.10.2015
comment
Похоже, вечно путаться уже не до. :-( Очень грустно, я многому научился оттуда! - person rossb83; 10.05.2019
comment
Это действительно позор. К счастью, в обратном пути все еще есть детали. https://web.archive.org/web/20190207151651/http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_rbtree.aspx - person Francis Stephens; 12.05.2019

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

Этот кеш будет переменной, локальной для вашего алгоритма вращения, поэтому ему не потребуется больше места в дереве или дорогостоящих дополнительных обходов.

person joshperry    schedule 27.07.2010

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

person unbeli    schedule 27.07.2010

Другим решением, помимо указателей на родителей и повторного запроса к родителю, является поддержка стека предков.

Предположим, кто-то хочет вставить 23 в следующее дерево:

Красное черное дерево

Обычно алгоритм вставки следующий:

  1. Найдите узел, где было бы 23, если бы он был в дереве

  2. Если 23 уже есть, верните ошибку

  3. Если 23 еще нет, поставьте его.

  4. Запустите программу восстановления баланса/окрашивания по мере необходимости.

Теперь, чтобы использовать подход со стеком, вы выделяете стек, достаточно большой, чтобы поддерживать один узел на каждом уровне вашего дерева (я думаю, что 2 * потолок (Log2 (количество)) + 2) должно вас охватывать. Вы даже можете сохранить стек, выделенный для вставки или удаления, и просто очищать его всякий раз, когда вы начинаете вставку.

Так что -- смотри в корень. Поместите его в стек. 23 больше, чем значение в корне, так что идите вправо. Теперь поместите текущий узел node (значение 21) в стек. Если 23 находится в дереве, он должен быть справа от текущего узла. Но узел справа от текущего узла является нулевым дозорным. Таким образом, этот null-sentinel должен быть заменен узлом с вашим значением. Родительский элемент - это элемент на вершине стека (последний раз помещенный), дедушка и бабушка - следующий в очереди... и т. д. Поскольку вы, кажется, учитесь... Java предоставляет вам интерфейс стека, поэтому вам не понадобится для разработки собственного стека для этого. Просто используйте их.

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

person Christopher Susie    schedule 02.07.2017