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

Я работаю над BST, который будет уравновешивать узлы в соответствии с их попаданиями и их элементами, где попадание - это атрибут, который увеличивается при обнаружении узла с помощью find(), contains() и т. Д. Корнем дерева является узел с наибольшим количеством просмотров. Весь мой код в порядке, кроме метода баланса, который уравновесит дерево после того, как я увеличу попадание. Я использую модифицированные методы поворота дерева AVL ( https://users.cs.fiu.edu/~weiss/dsj2/code/weiss/nonstandard/Rotations.java), где я не сравниваю элементы, а просто совпадения узел. Я не могу заставить его работать независимо от того, что я пытаюсь, я не могу заставить дерево сбалансироваться правильно. Вот мой код:

 public void balanceTree() {
    balanceTree(root);
}

private void balanceTree(Node node) {

    if (node.left.getHits() <= node.getHits() && node.right.getHits() <= node.getHits()) {
        return;
    } else if (node.left.getHits() > node.getHits()) {
        node = rotateWithLeftChild(node);

    } else if (node.right.getHits() > node.getHits()) {
        node = rotateWithRightChild(node);

    }


}

static Node rotateWithLeftChild(Node k2) {
    Node k1 = k2.left;
    k2.left = k1.right;
    k1.right = k2;
    return k1;
}

static Node rotateWithRightChild(Node k1) {
    Node k2 = k1.right;
    k1.right = k2.left;
    k2.left = k1;
    return k2;
}

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

2 ответа

Решение

В Java нельзя изменить переданный параметр, поэтому необходимо вернуть новое значение узла.

public void balanceTree() {
    root = balanceTree(root);
}

private Node balanceTree(Node node) 
    if (node.left.getHits() <= node.getHits()
            && node.right.getHits() <= node.getHits()) {
        node.left = balanceTree(node.left);
        node.right = balanceTree(node.right);
    } else if (node.left.getHits() > node.getHits()) {
        node = rotateWithLeftChild(node);
    } else if (node.right.getHits() > node.getHits()) {
        node = rotateWithRightChild(node);
    }
    return node;
}

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

Без вращения нужно возвращаться.

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

Более тревожным для такого рода алгоритмов модификации является то, что он может не стабилизироваться: сохраняя ребалансировку. Но это вы обязательно заметите на тестировании.

Есть 2 вещи не так с этим кодом.
1) Я скучаю по структуре этого Дерева. Таким образом, они должны быть отсортированы в зависимости от их HitCount, а не то, что в основном список / коллекция отсортирована по HitCount?

Прямо сейчас вы меняете узлы их левым и правым узлами, если у них более высокий счетчик попаданий, чем у них самого. Итак, представьте, что 2 узла [A, B] A имеют 1 hitCount, а B имеет 2 hitCount. Итак, при сортировке (вы, вероятно, будете перебирать узлы):
Начните ситуацию: [A, B]

Первая сортировка: A имеет меньшее значение HitCount, чем B, поэтому поменяйте местами с правым. Результат = [B, A]

Второй вид: A имеет меньшее значение HitCount, чем B, поэтому поменяйте местами с левой. Результат = [A, B]

Где мы заканчиваем? Лучшей идеей может быть использование List и сортировка Nodes на основе их hitCount. Таким образом, вам не придется связываться со всем этим.

2) Ваши методы обмена не работают так, как вы думаете. Внимательно посмотрите на это:

 static Node rotateWithLeftChild(Node k2) {
     Node k1 = k2.left;
     k2.left = k1.right;
     k1.right = k2;
     return k1;
 }

 // exact the same results:
 static Node rotateWithLeftChild(Node k2)
 {
     k2.left = k2;
     return k2.left;
 }

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

 static Node rotateWithLeftChild(Node k2)
 {
     Node k1 = k2.left;
     k1.right = k2.right;
     k2.left = k1.left;
     k1.left = k2;
     k2.right = k1;
     return k1;
 }

И, конечно, обратное относится к rotateWithRightChild.

Я надеюсь, что это немного помогло вам.

Редактировать:

Как реализовать Список / Коллекция для заказа? Когда узел добавлен в дерево, просто добавьте узел в Lisf/Collection. И когда вы хотите отсортировать узлы, просто используйте это:

 //myNodesCollection is the List/Collection containing all the nodes.
 static void sortByHitCount()
 {
     Collections.sort(myNodesCollection, (n1, n2) -> n1.getHits() - n2.getHits());
 }

Это может показаться сложным, но этот метод выполняет всю сортировку за вас. Первый параметр - это список / коллекция, которую вы хотите отсортировать. Второй параметр - это компаратор, который в этом случае сравнивает каждый узел на основе их hitCount.

Документация: https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html

Другие вопросы по тегам