Я должен найти kth самый маленький элемент в дереве двоичного поиска, не используя статической / глобальной переменной. Как достигнуть его эффективно? Решение, которое я знаю, делает операцию в O (n), худший случай, так как я планирую сделать inorder обход всего дерева. Но в глубине души я чувствую, что не использую свойство BST здесь. Мое допускаемое решение правильно или является там лучшим доступным?
Вот лишь краткое описание идеи:
В BST левое поддерево узла T
содержит только элементы, меньшие, чем значение хранится в T
. Если k
меньше количества элементов в левом поддереве, k
-й наименьший элемент должен принадлежать левому поддереву. В противном случае, если k
больше, то самый маленький элемент k
находится в правом поддереве.
Мы можем дополнить BST, чтобы каждый узел в нем сохранял количество элементов в своем левом поддереве (предположим, что левое поддерево данного узла включает этот узел). С помощью этой части информации легко пройти по дереву, неоднократно запрашивая количество элементов в левом поддереве, чтобы решить, делать ли рекурсию в левое или правое поддерево.
Теперь предположим, что мы находимся в узле T:
T
. k
-го наименьшего.Итак, мы сводим задачу к поиску k - num_elements (левое поддерево T)
наименьшего элемента правого поддерева. k
-й наименьший находится где-то в левом поддереве, поэтому мы сводим проблему к поиску k
-й наименьший элемент в левом поддереве. Анализ сложности:
Это занимает O (глубина узла)
времени, что составляет O (log n)
в худшем случае на сбалансированной BST или ] O (log n)
в среднем для случайного BST.
BST требует хранения O (n)
, а для хранения информации о количестве элементов требуется еще O (n)
. Все операции BST занимают O (глубина узла)
времени, и требуется O (глубина узла)
дополнительного времени для поддержания информации о «количестве элементов» для вставки, удаления или поворота. узлов. Следовательно, хранение информации о количестве элементов в левом поддереве сохраняет пространственную и временную сложность BST.
Учитывая простое двоичное дерево поиска, все, что вы можете сделать, - это начать с наименьшего и подняться вверх, чтобы найти нужный узел.
Если вы собираетесь делать это очень часто, вы можете добавить атрибут к каждому узлу, показывающий, сколько узлов находится в его левом поддереве. Используя это, вы можете спуститься по дереву прямо к нужному узлу.
public int ReturnKthSmallestElement1(int k)
{
Node node = Root;
int count = k;
int sizeOfLeftSubtree = 0;
while(node != null)
{
sizeOfLeftSubtree = node.SizeOfLeftSubtree();
if (sizeOfLeftSubtree + 1 == count)
return node.Value;
else if (sizeOfLeftSubtree < count)
{
node = node.Right;
count -= sizeOfLeftSubtree+1;
}
else
{
node = node.Left;
}
}
return -1;
}
это моя реализация на C #, основанная на алгоритме выше, я просто подумал, что выложу его, чтобы люди могли лучше понять, что он работает для меня
спасибо IVlad
Для не сбалансированного дерева поиска требуется O(n).
Для сбалансированного дерева поиска требуется O(k + log n) в худшем случае, но только O(k) в амортизированном смысле.
Наличие и управление дополнительным целым числом для каждого узла: размером поддерева дает O(log n) временную сложность. Такое сбалансированное дерево поиска обычно называют RankTree.
В целом, существуют решения (основанные не на дереве).
С уважением.
Это то, что я подумал, и это работает. Он будет работать в o (log n)
public static int FindkThSmallestElemet(Node root, int k)
{
int count = 0;
Node current = root;
while (current != null)
{
count++;
current = current.left;
}
current = root;
while (current != null)
{
if (count == k)
return current.data;
else
{
current = current.left;
count--;
}
}
return -1;
} // end of function FindkThSmallestElemet
Для двоичного дерева поиска обход по порядку вернет элементы ... по порядку.
Просто выполните обход в порядке и остановитесь после обхода k элементов.
O (1) для постоянных значений k.