Как определить общие диапазоны значений (включая открытые диапазоны) для проверки общего двоичного дерева поиска с ключами произвольного типа - PullRequest
0 голосов
/ 31 марта 2019

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

Ниже приведен мой код, чтобы сделать это для целого числа.

 public boolean checkBST(Node root) {
    int min = Integer.MIN_VALUE;
    int max = Integer.MAX_VALUE;
    return validateBST(root, min, max);
  }

Ссылка: https://en.wikipedia.org/wiki/Binary_search_tree#Verification

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

Это легко сделать с обертками числового примитивного типа, такими как Integer (пример выше) или Double, если вы фиксируете определенныйтип элемента.Однако мне нужно обобщить этот подход, чтобы он работал с любым данным типом T (это может быть Number или что-то совершенно другое).

Мы можем предположить, что T является Comparable<? extends T> или что соответствующий компаратор передается при построении дерева.

Как я могу это сделать?

Ответы [ 2 ]

1 голос
/ 01 апреля 2019

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

К сожалению, в общем случае произвольный T не обязательно имеет инстанцируемый минимум и максимум. На самом деле int и Integer делают только потому, что из-за ограничения представления они не могут быть сколь угодно большими или маленькими.

Например, для String можно утверждать, что тогда пустая строка ("") является минимумом, безусловно, меньше или равна любой другой String, использующей ее естественный порядок, но какой будет максимальная строка. Вероятно, это будет бесконечно длинное повторение максимального символа Юникода, поэтому вы не можете создать такой объект.

Однако это не должно помешать вам определить диапазоны, которые включают любое значение или они открыты на одном конце (т. Е. У них есть минимум, но не максимум или наоборот).

Например, если диапазон должен быть указан как два аргумента в сигнатуре метода, который будет использовать такой диапазон, тогда вы можете просто указать null, чтобы указать, что min или max не существует, то есть этот конец диапазона открыт.

Мне кажется, что это будет хорошо работать с вашим valideteBST, поскольку он уже имеет эти два параметра.

class BST<T extends Comparable<T>> {
   // ...
   public boolean checkBST(Node<T> root) {
      return validateBST(root, null, null);
   }
   // ...
   boolean validateBST(Node<T> node, T min, T max) {
         if (node == null) {
            // nothing to do here.
            return true;
         }
         final T value = node.getValue();
         if (min != null && min.compareTo(value) >= 0) {
             return false;
         } else if (max != null && max.compareTo(value) <= 0) {
             return false;
         } else {
             return validateBST(node.getLeft(), min, value) &&
                    validateBST(node.getRight(), value, max);
         }
   }
   // ...
}

Теперь некоторым людям может не понравиться видимое использование null для этого. В таком случае вам может потребоваться определить класс Range<T>, который его инкапсулирует:

public class Range<T extends Comparable<T>> {
    private T min;
    private T max;

    private Range(T min, T max) {
       this.min = min;
       this.max = max;
    }

    public static <T> of(T min, T max) {
       Objects.requiresNonNull(min);
       Objects.requiresNonNull(max);
       return new Range(min, max);
    }

    public static <T> from(T min) {
       Objects.requiresNonNull(min);
       return new Range(min, null);
    }

    public static <T> to(T max) {
       Objects.requiresNonNull(max);
       return new Range(null, max);
    }

    public static <T> all() {
       return new Range(null, null);
    }

    public Range<T> subRangeTo(T max) {
       Objects.requiresNonNull(max);
       return new Range<>(this.min, max);
    }

    public Range<T> subRangeFrom(T min) {
       Objects.requiresNonNull(min);
       return new Range<>(min, this.max);
    }

    public boolean encloses(T value) {
       Objects.requiresNonNull(value);
       return (min == null || min.compareTo(value) < 0) 
            &&  (max == null || max.compareTo(value) > 0);
    }
}

Тогда код в validate более тривиален:

   // ...
   public boolean checkBST(Node<T> root) {
     return validateBST(root, Range.all());
   }
   // ...
   boolean validateBST(Node<T> node, Range<T> range) {
         if (node == null) {
            return true;
         }
         if (!range.encloses(node.getValue)) {
            return false;
         } else {
            return validateBST(node.getLeft(), Range.subRangeTo(value)) 
                   && validateBST(node.getRight(), Range.subRangeFrom(value));
         }
   }
   // ...

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

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

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

1 голос
/ 01 апреля 2019

Для сравнения объектов вам нужно, чтобы они реализовали Comparable, поэтому определите свой тип узла как подкласс Comparable:

class Node<Q extends Comparable<Q>>{

    private final Q value;
    private Node left, right;   

    Node getLeft() {
        return left;
    }

    Node getRight() {
        return right;
    }

    Node(Q value){
        this.value = value;
    }

    Q getValue(){
        return value;
    }
}

Когда Q реализует сопоставимые объекты, вы можете сравнить два объекта, дляпример:

Node<String> minNode = new Node<>("Z");
Node<String> maxNode = new Node<>("A");
Node<String> aNode = new Node<>("L");
System.out.println(aNode.getValue().compareTo(minNode.getValue())< 0 &&
                            aNode.getValue().compareTo(maxNode.getValue()) > 0 );

Если вы сможете сравнить, вы сможете определить boolean isBST(Node node, Node minNode, Node maxNode)

Редактировать: Если ваш вопрос касается "универсальной версии Integer.MIN_VALUE и Integer.MAX_VALUE для любого заданного T ":Я не думаю, что это практически необходимо.Вы можете перебирать весь график, находить минимальные, максимальные значения и использовать эти значения.

...