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

Для данного двоичного дерева найдите наибольшее поддерево, которое также является деревом двоичного поиска?

Пример:

Input:

                   10
               /         \
             50           150
            /  \         /   \
          25    75     200    20
         / \   / \    /  \    / \
        15 35 65 30  120 135 155 250 

Вывод:

                   50
                  /   \
                 25   75
                / \   /
               15 35  65

Ответ 1

Этот ответ ранее содержал алгоритм O (n log n), основанный на деревьях ссылок/выреза. Вот более простое решение O (n).

Ядро - это процедура, которая принимает node, уникальный максимальный BSST, укорененный в его левом дочернем элементе, уникальный максимальный BSST, внедренный в его правый дочерний элемент, и указатели на самые левые и самые правые элементы этих BSST, Он разрушает свои входы (можно избежать с постоянными структурами данных) и создает уникальный максимальный BSST, внедренный в данный node, вместе с его минимальными и максимальными элементами. Все узлы BSST аннотируются количеством потомков. Как и раньше, эта процедура многократно вызывается из обхода после заказа. Чтобы восстановить поддерево, запомните корень самого большого BSST; восстановление его требует лишь простого обхода.

Я буду рассматривать только левый BSST; право симметрично. Если корень левой BSST больше, чем новый корень, то все поддерево удаляется, а новый корень теперь остается самым большим. В противном случае старая левая часть node по-прежнему остается самой большой. Начиная с самого правого node левого BSST и перемещаясь вверх, найдите первый node, который меньше или равен корню. Его правильный ребенок должен быть удален; обратите внимание, что из-за свойства BST других узлов не нужно! Перейдите к корню левой BSST, обновив подсчеты, чтобы отразить удаление.

Причина, по которой это O (n), состоит в том, что, несмотря на цикл, каждое ребро в исходном дереве по существу проходит только один раз.


EDIT: в совокупности пройденные пути - это максимальные прямые пути в BST, за исключением левого позвоночника и правого позвоночника. Например, на входе

              H
             / \
            /   \
           /     \
          /       \
         /         \
        /           \
       /             \
      D               L
     / \             / \
    /   \           /   \
   /     \         /     \
  B       F       J       N
 / \     / \     / \     / \
A   C   E   G   I   K   M   O

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

              H
             / \
            /   \
           /     \
          /       \
         /         \
        /           \
       /             \
      D               L
     / h             h \
    /   h           h   \
   /     h         h     \
  B       F       J       N
 / d     d h     h l     l \
A   C   E   G   I   K   M   O

Ответ 2

Предыдущий алгоритм (см. ревизии) был O(n^2) - мы можем обобщить его на O(n log n), заметив факты, что:

  • Если b является корнем наибольшего BST и b.left.value < b.value, то b.left также находится в BST (тот же для b.right.value ≥ b.value)
  • Если b является корнем наибольшего BST и a также находится в BST, то каждый node между a и b находится в BST.

Итак, если c находится между a и b, а c не находится в BST, основанной на b, ни одна из них (из-за (2.)). Используя этот факт, мы можем легко определить, есть ли node в BST, внедренном любым предком. Мы сделаем это, передав node в нашу функцию вместе со списком своих предков и связанными с ними min/maxValues, которые текущий ребенок node должен был бы удовлетворять, если бы действительно этот предок был корнем самого большого BST (мы будем называть этот список ancestorList). Мы сохраним весь набор потенциальных корней в overallRootsList

Определим структуру, называемую потенциаломRoot следующим образом:

Каждый потенциалRoot содержит следующие значения:
        * node: node, который мы рассматриваем для корня BST         * minValue и maxValue: диапазон другой node должен находиться между частью BST, основанной на node (разная для каждого node)
        * subNodes: список остальных узлов в самом большом BST, внедренном node

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

FindLargestBST(node, ancestorList):
    leftList, rightList = empty lists
    for each potentialRoot in ancestorList:
        if potentialRoot.minValue < node.Value ≤ potentialRoot.maxValue:
            add node to potentialRoot.subNodes (due to (1.))
            (note that the following copies contain references, not copies, of subNodes)
            add copy of potentialRoot to leftList, setting maxValue = node.Value
            add copy of potentialRoot to rightList, setting minValue = node.Value

    add the potentialRoot (node, -∞, +∞) to leftList, rightList, and overallRootsList
    FindLargestBST(node.left, leftList)
    FindLargestBST(node.right, rightList)

В конце overallRootsList будет список n потенциальныхRoots, каждый со списком поднодов. Тот, у которого самый большой список подназваний, является вашим BST.

Поскольку существуют < treeHeight в ancestorList, тогда (при условии, что дерево сбалансировано), алгоритм работает в O(n log n)

Ответ 3

Интересный вопрос!

Моя предыдущая попытка была нехорошо ошибочной!

Вот еще одна попытка (надеюсь, исправить на этот раз).

Я предполагаю, что дерево связано.

Предположим, что для каждого дерева node n у вас был набор потомков n, S n с тем свойством, что

  • Для каждого члена x из S n уникальный путь от n до x является двоичным деревом поиска (это только путь, но вы все равно можете считать его деревом).

  • Для каждого потомка y из x, так что путь от n до y является BST, y находится в S n.

Набор узлов S n, дает вам наибольшую BST, корневую в n.

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

Когда мы посещаем node, мы поднимаемся по пути и проверяем, выполнено ли свойство BST для этого сегмента пути, который дошел до него. Если это так, мы добавим текущий node в соответствующий набор node пути, к которому мы только что подошли. Мы перестаем ходить по пути, когда нарушается свойство BST. Проверка того, что сегмент пути, который мы шли до сих пор, - это BST, может быть выполнено в O (1) раз, для времени обработки O (длина пути), для каждого node.

В конце каждого node будет присвоено соответствующее S n. Теперь мы можем пройти дерево и выбрать node с наибольшим значением S n.

Время, затраченное на это, - это сумма глубин узлов (в худшем случае), а это O (nlogn) в среднем случае (см. раздел 5.2.4 http://www.toves.org/books/data/ch05-trees/index.html), но O (n ^ 2) в худшем случае.

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

Псевдокод может выглядеть примерно так:

static Tree void LargestBST(Tree t)
{
    LargestBST(t, new List<Pair>());
    // Walk the tree and return the largest subtree with max |S_n|.
}

static Tree LargestBST(Tree t, List<Pair> path)
{
    if (t == null) return;

    t.Set.Add(t.Value);

    int value = t.Value;
    int maxVal = value;
    int minVal = value;

    foreach (Pair p in path)
    {
        if (p.isRight)
        {
            if (minVal < p.node.Value)
            {
                break;
            }
        }

        if (!p.isRight)
        {
            if (maxVal > p.node.Value)
            {
                break;
            }
        }

        p.node.Set.Add(t.Value);

        if (p.node.Value <= minVal)
        {
            minVal = p.node.Value;
        }

        if (p.node.Value >= maxVal)
        {
            maxVal = p.node.Value;
        }
    }

    Pair pl = new Pair();
    pl.node = t;
    pl.isRight = false;

    path.Insert(0, pl);
    LargestBST(t.Left, path);

    path.RemoveAt(0);

    Pair pr = new Pair();
    pr.node = t;
    pr.isRight = true;

    path.Insert(0, pr);

    LargestBST(t.Right, path);

    path.RemoveAt(0);

}

Ответ 4

КРУПНЕЙШЕЕ ДВОЙНОЕ ДИСКИ БИНАРЕНИЯ В БИНАРНОМ ДЕРЕВО:

Мы можем подойти к этой проблеме двумя способами:

i) Наибольший BST не индуцирован (из node все его дети не должны удовлетворять условию BST)

ii) Самый большой BST-индуцированный (из node все его дети будут удовлетворять условию BST)

Мы обсудим здесь самый большой BST (Not Induced). Мы будем придерживаться подхода нижнего уровня (обход по порядку), чтобы решить эту проблему.

a) Потяните лист node

b) Дерево node (из листа) вернет объект TreeNodeHelper, который имеет в нем следующие поля.

public static class TreeNodeHelper {
        TreeNode node;
        int nodes;
        Integer maxValue;
        Integer minValue;
        boolean isBST;


        public TreeNodeHelper() {}

        public TreeNodeHelper(TreeNode node, int nodes, Integer maxValue, Integer minValue, boolean isBST) {
            this.node = node;
            this.nodes = nodes;
            this.maxValue = maxValue;
            this.minValue = minValue;
            this.isBST = isBST;
        }      
    }

c) Первоначально из листа node, nodes = 1, isBST = true, minValue = maxValue = node.data. Кроме того, количество узлов будет увеличено, если оно удовлетворяет условию BST.

d) С помощью этого мы проверим условие BST с текущим значением node. И мы будем повторять то же самое до корня.

e) Из каждого node возвращаются два объекта. один для последнего максимального BST и другой для текущего BST, удовлетворяющего узлам. Поэтому из каждого node (над листом) (2 + 2) = 4 (2 для левого поддерева и 2 для правого под дерева) объекты будут сравниваться, а два будут возвращены.

f) Конечный максимальный node объект от root будет самым большим BST

Проблема:

В этом подходе есть проблема. Следуя этому подходу, если поддерево не удовлетворяет условию BST с текущим node, мы не можем просто игнорировать поддерево (даже у него меньше число узлов). Например

 55
  \
   75
  /  \
 27  89
    /  \
   26  95
      /  \
     23  105
         /  \
        20  110

Из листовых узлов (20,110) объекты будут проверяться с помощью node (105), он удовлетворяет условию. Но когда он достигает node (95), лист node (20) не удовлетворяет условию BST. Поскольку это решение для BST (не индуцировано), мы не должны игнорировать node (105) и node (110), который удовлетворяет условию. Поэтому из node (95) нам нужно снова вернуться к тестированию условия BST и поймать эти узлы (105, 110).

Полный код для этой реализации доступен по этой ссылке

https://github.com/dineshappavoo/Implementation/tree/master/LARGEST_BST_IN_BT_NOT_INDUCED_VER1.0

Ответ 5

Двоичное дерево поиска даст вам отсортированный результат, если вы совершаете обход IN-ORDER. Итак, выполните обход в порядке для всего двоичного дерева. Самая длинная отсортированная последовательность - это ваше самое большое двоичное дерево поиска.

  • Выполняйте обход элементов (ВИЗИТ ВЛЕВО, ПОСЕЩАЙТЕ КОРНУЮ, ПОСЕТИТЕ ВПРАВО)
  • При этом получайте данные node, сравните ли предыдущие данные node меньше, чем следующие данные. Если это так, увеличивайте счетчик на 1. Сохраните старт node.
  • Когда сравнение не выполняется, сохраните конец node и reset счетчик 0
  • Сохраните эту информацию (счетчик, начало, конец) node в структуре массива, чтобы впоследствии найти, которая имеет максимальное значение, и это даст вам самое длинное двоичное дерево поиска

Ответ 6

GetLargestSortedBinarySubtree(thisNode, ref OverallBestTree)
    if thisNode == null
        Return null
    LeftLargest = GetLargestSortedBinarySubtree(thisNode.LeftNode, ref OverallBestTree)
    RightLargest = GetLargestSortedBinarySubtree(thisNode.RightNode, ref OverallBestTree)
    if LeftLargest.Max < thisNode.Value & RightLargest.Min > thisNode.Value
        currentBestTree = new BinaryTree(LeftLargest, thisNode.Value, RightLargest)
    else if LeftLargest.Max < thisNode.Value
        currentBestTree = new BinaryTree(LeftLargest, thisNode.Value, null)
    else if RightLargest.Min > thisNode.Value
        currentBestTree = new BinaryTree(null, thisNode.Value, RightLargest)
    else
        currentBestTree = new BinaryTree(null, thisNode.Value, null)
    if (currentBestTree.Size > OverallBestTree.Size)
        OverallBestTree = currentBestTree
    return currentBestTree

Как отметил BlueRaja, этот алгоритм неверен.

На самом деле его следует называть GetLargestSortedBinarySubtreeThatCanBeRecursivelyConstructedFromMaximalSortedSubtrees.

Ответ 7

root(Tree L A R) = A

MaxBST(NULL) = (true, 0, NULL)
MaxBST(Tree L A R as T) = 
  let
    # Look at both children
    (L_is_BST, L_size, L_sub) = MaxBST(L)
    (R_is_BST, R_size, R_sub) = MaxBST(R)
  in
  # If they're both good, then this node might be good too
  if L_is_BST and R_is_BST and (L == NULL or root(L) < A) and (R == NULL or A < root(R))
  then (true, 1 + L_size + R_size, T)
  else
       # This node is no good, so give back the best our children had to offer
       (false, max(L_size, R_size), if L_size > R_size then L_sub else R_sub)

Выглядит каждое дерево node ровно один раз, поэтому работает в O (N).

Изменить: Crud, это не означает, что он может оставить некоторые части поддерева. Когда я читал поддерево, я предположил, что "все дерево коренится в некотором node". Я могу вернуться, чтобы исправить это позже.