Мне нужно найти k-й наименьший элемент в двоичном дереве поиска без использования какой-либо статической / глобальной переменной. Как этого добиться эффективно? Решение, которое я имею в виду, - это выполнение операции за O (n), наихудший случай, поскольку я планирую выполнить обход всего дерева по порядку. Но в глубине души я чувствую, что не использую свойство BST здесь. Правильно ли мое предположительное решение или есть лучшее?
112
Ответы:
Вот лишь краткое изложение идеи:
В BST левое поддерево узла
T
содержит только элементы, меньшие, чем значение, хранящееся вT
. Еслиk
меньше, чем количество элементов в левом поддереве,k
наименьший элемент должен принадлежать левому поддереву. В противном случае, еслиk
больше, тоk
наименьший элемент находится в правом поддереве.Мы можем дополнить BST, чтобы каждый узел в нем сохранял количество элементов в своем левом поддереве (предположим, что левое поддерево данного узла включает этот узел). С помощью этой части информации легко пройти по дереву, неоднократно запрашивая количество элементов в левом поддереве, чтобы решить, делать ли рекурсию в левое или правое поддерево.
Теперь предположим, что мы находимся в узле T:
T
.k
th наименьшего. Итак, мы сводим задачу к поискуk - num_elements(left subtree of T)
наименьшего элемента правого поддерева.k
наименьший поk
размеру элемент находится где-то в левом поддереве, поэтому мы сводим проблему к поиску th наименьшего элемента в левом поддереве.Анализ сложности:
Это требует
O(depth of node)
времени, чтоO(log n)
в худшем случае для сбалансированного BST илиO(log n)
в среднем для случайного BST.Для BST требуется
O(n)
хранилище, аO(n)
для хранения информации о количестве элементов требуется другое . Все операции BST требуютO(depth of node)
времени, и требуетсяO(depth of node)
дополнительное время для поддержания информации о «количестве элементов» для вставки, удаления или вращения узлов. Следовательно, хранение информации о количестве элементов в левом поддереве сохраняет пространственную и временную сложность BST.источник
Более простым решением было бы выполнить обход по порядку и отслеживать элемент, который в настоящее время должен быть напечатан (без его печати). Когда мы дойдем до k, распечатайте элемент и пропустите оставшуюся часть обхода дерева.
источник
это моя реализация на C # на основе алгоритма выше, я просто подумал, что опубликую его, чтобы люди могли лучше понять, что он работает для меня
спасибо IVlad
источник
Более простым решением было бы выполнить обход по порядку и отслеживать элемент, который в настоящее время должен быть напечатан, с помощью счетчика k. Когда мы дойдем до k, распечатайте элемент. Время выполнения - O (n). Помните, что тип возвращаемого значения функции не может быть недействительным, он должен возвращать свое обновленное значение k после каждого рекурсивного вызова. Лучшим решением для этого был бы расширенный BST с отсортированным значением позиции в каждом узле.
источник
// добавляем версию java без рекурсии
источник
Вы можете использовать итеративный обход по порядку: http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal с простой проверкой k-го элемента после извлечения узла из стека.
источник
Имея только простое двоичное дерево поиска, все, что вы можете сделать, это начать с самого маленького и пройти вверх, чтобы найти нужный узел.
Если вы собираетесь делать это очень часто, вы можете добавить атрибут к каждому узлу, показывающий, сколько узлов находится в его левом поддереве. Используя это, вы можете спуститься по дереву прямо к нужному узлу.
источник
Рекурсивная прогулка по порядку со счетчиком
Идея аналогична решению @prasadvk, но у него есть некоторые недостатки (см. Примечания ниже), поэтому я публикую это как отдельный ответ.
Примечания (и отличия от решения @prasadvk):
if( counter == k )
test требуется в трех местах: (а) после левого поддерева, (б) после корня и (в) после правого поддерева. Это необходимо для того, чтобы обеспечить обнаружение k-го элемента для всех местоположений , т.е. независимо от того, в каком поддереве он находится.if( result == -1 )
test требуется для обеспечения печати только элемента результата , в противном случае печатаются все элементы, начиная с k-го наименьшего до корня.источник
O(k + d)
, гдеd
- максимальная глубина дерева. Поэтому он использует глобальную переменную,counter
но это незаконно для этого вопроса.Для не сбалансирован поиска дерева, она занимает O (N) .
Для сбалансированного дерева поиска требуется O (k + log n) в худшем случае, но только O (k) в амортизированном смысле.
Наличие и управление дополнительным целым числом для каждого узла: размер поддерева дает временную сложность O (log n) . Такое сбалансированное дерево поиска обычно называется RankTree.
В общем, решения есть (основанные не на дереве).
С уважением.
источник
Это хорошо работает: status: это массив, который определяет, найден ли элемент. k: k-й элемент, который нужно найти. count: отслеживает количество узлов, пройденных во время обхода дерева.
источник
Хотя это определенно не оптимальное решение проблемы, это еще одно потенциальное решение, которое, как я думал, может показаться некоторым людям интересным:
источник
подпись:
позвонить как:
определение:
источник
Ну вот мои 2 цента ...
источник
Это то, что я думал, и это работает. Он запустится через o (log n)
источник
Что ж, мы можем просто использовать обход по порядку и поместить посещенный элемент в стек. нажмите k количество раз, чтобы получить ответ.
мы также можем остановиться после k элементов
источник
Решение для полного случая BST: -
источник
Ядро Linux имеет превосходную расширенную красно-черную древовидную структуру, которая поддерживает операции на основе ранга в O (log n) в linux / lib / rbtree.c.
Очень грубый порт Java также можно найти по адресу http://code.google.com/p/refolding/source/browse/trunk/core/src/main/java/it/unibo/refolding/alg/RbTree.java , вместе с RbRoot.java и RbNode.java. N'-й элемент можно получить, вызвав RbNode.nth (узел RbNode, int n), передав его в корень дерева.
источник
Вот краткая версия на C #, которая возвращает k-й наименьший элемент, но требует передачи k в качестве аргумента ref (это тот же подход, что и @prasadvk):
Это O (журнал N) , чтобы найти в наименьший узел, а затем вывода (к) , чтобы пройти к к-го узла, так что это O (к + войти п).
источник
http://www.geeksforgeeks.org/archives/10379
это точный ответ на этот вопрос: -
1. с использованием обхода по порядку за время O (n) 2. с использованием расширенного дерева за время k + log n
источник
Лучшего алгоритма я не нашел ... решил написать :) Поправьте меня, если это не так.
}
источник
Вот код Java,
max (Node root, int k) - найти k-й по величине
min (Node root, int k) - найти kth наименьший
источник
это тоже сработает. просто вызовите функцию с maxNode в дереве
def k_largest (self, node, k): if k <0: return None
if k == 0: return node else: k - = 1 return self.k_largest (self.predecessor (node), k)
источник
Я думаю, что это лучше, чем принятый ответ, потому что не нужно изменять исходный узел дерева для хранения количества его дочерних узлов.
Нам просто нужно использовать обход по порядку, чтобы подсчитать наименьший узел слева направо, прекратить поиск, когда счетчик станет равным K.
источник
Лучший подход уже есть, но я бы хотел добавить простой код для этого
}
источник
Использование вспомогательного класса Result для отслеживания того, найден ли узел и есть ли текущий k.
источник
Сложность времени решения Python: O (n) Сложность пространства: O (1)
Идея состоит в том, чтобы использовать Morris Inorder Traversal
источник
Я написал аккуратную функцию для вычисления k-го наименьшего элемента. Я использую обход по порядку и останавливаюсь, когда достигает k-го наименьшего элемента.
источник
источник
источник
}
источник