Muszę znaleźć k-ty najmniejszy element w drzewie wyszukiwania binarnego bez użycia zmiennej statycznej / globalnej. Jak to skutecznie osiągnąć? Rozwiązaniem, które mam na myśli, jest wykonanie operacji w O (n), najgorszym przypadku, ponieważ planuję wykonać wewnętrzne przejście całego drzewa. Ale w głębi duszy czuję, że nie używam tutaj właściwości BST. Czy moje założenie jest poprawne, czy jest dostępne lepsze?
algorithm
data-structures
binary-tree
binary-search
przechwałek
źródło
źródło
Odpowiedzi:
Oto tylko zarys pomysłu:
W BST lewe poddrzewo węzła
T
zawiera tylko elementy mniejsze niż wartość przechowywana wT
. Jeślik
jest mniejsza niż liczba elementów w lewym poddrzewie, tok
najmniejszy element musi należeć do lewego poddrzewa. W przeciwnym razie, jeślik
jest większy, tok
najmniejszy element znajduje się w prawym poddrzewie.Możemy rozszerzyć BST, aby każdy węzeł w nim przechował liczbę elementów w swoim lewym poddrzewie (załóżmy, że lewe poddrzewo danego węzła zawiera ten węzeł). Dzięki tej informacji łatwo jest przejść przez drzewo, wielokrotnie prosząc o liczbę elementów w lewym poddrzewie, aby zdecydować, czy powtórzyć w lewym, czy prawym poddrzewie.
Teraz załóżmy, że jesteśmy w węźle T:
T
.k
najmniejsze. Tak więc sprowadzamy problem do znalezieniak - num_elements(left subtree of T)
najmniejszego elementu odpowiedniego poddrzewa.k
najmniejsze znajduje się gdzieś w lewym poddrzewie, więc sprowadzamy problem do znalezieniak
tego najmniejszego elementu w lewym poddrzewie.Analiza złożoności:
To wymaga
O(depth of node)
czasu, coO(log n)
w najgorszym przypadku ma miejsce na zrównoważonym BST lubO(log n)
średnio w przypadku losowego BST.BST wymaga
O(n)
pamięci, a innyO(n)
do przechowywania informacji o liczbie elementów. Wszystkie operacje BST wymagająO(depth of node)
czasu, aO(depth of node)
utrzymanie informacji o liczbie elementów do wstawienia, usunięcia lub obrotu węzłów zajmuje więcej czasu. Dlatego przechowywanie informacji o liczbie elementów w lewym poddrzewie zachowuje złożoność przestrzenną i czasową BST.źródło
Prostszym rozwiązaniem byłoby wykonanie przejścia w kolejności i śledzenie aktualnie drukowanego elementu (bez jego drukowania). Kiedy osiągniemy k, wypisz element i pomiń resztę przechodzenia po drzewie.
źródło
to jest moja implementacja w C # w oparciu o powyższy algorytm, po prostu pomyślałem, że opublikuję go, aby ludzie mogli lepiej zrozumieć, że działa dla mnie
dziękuję IVlad
źródło
Prostszym rozwiązaniem byłoby wykonanie przejścia w kolejności i śledzenie aktualnie drukowanego elementu za pomocą licznika k. Kiedy osiągniemy k, wydrukuj element. Środowisko wykonawcze to O (n). Pamiętaj, że zwracany typ funkcji nie może być void, musi zwracać zaktualizowaną wartość k po każdym wywołaniu rekurencyjnym. Lepszym rozwiązaniem byłby rozszerzony BST z posortowaną wartością pozycji w każdym węźle.
źródło
// dodaj wersję java bez rekursji
źródło
Możesz użyć iteracyjnego przechodzenia w kolejności: http://en.wikipedia.org/wiki/Tree_traversal#Iterative_Traversal z prostym sprawdzeniem k-tego elementu po wyjęciu węzła ze stosu.
źródło
Biorąc pod uwagę tylko zwykłe drzewo wyszukiwania binarnego, prawie wszystko, co możesz zrobić, to zacząć od najmniejszego i przejść w górę, aby znaleźć właściwy węzeł.
Jeśli masz zamiar robić to bardzo często, możesz dodać atrybut do każdego węzła oznaczający, ile węzłów znajduje się w jego lewym poddrzewie. Używając tego, możesz zejść z drzewa bezpośrednio do właściwego węzła.
źródło
Rekurencyjny spacer w kolejności z licznikiem
Pomysł jest podobny do rozwiązania @prasadvk, ale ma pewne wady (patrz uwagi poniżej), więc zamieszczam to jako osobną odpowiedź.
Uwagi (i różnice w stosunku do rozwiązania @ prasadvk):
if( counter == k )
test jest wymagany w trzech miejscach: (a) za lewym poddrzewem, (b) za korzeniem i (c) po prawym poddrzewie. Ma to na celu zapewnienie, że k-ty element zostanie wykryty we wszystkich lokalizacjach , tj. Niezależnie od poddrzewa, w którym się znajduje.if( result == -1 )
wymagany jest test, aby upewnić się, że drukowany jest tylko element wynikowy , w przeciwnym razie drukowane są wszystkie elementy, począwszy od k-tego najmniejszego do korzenia.źródło
O(k + d)
, gdzied
jest maksymalna głębokość drzewa. Dlatego używa zmiennej globalnej,counter
ale jest to nielegalne w przypadku tego pytania.Za nie wynosi przeszukiwania drzewa, zajmuje O (n) .
W przypadku zrównoważonego drzewa wyszukiwania, w najgorszym przypadku wymaga O (k + log n), ale tylko O (k) w amortyzacji sensie.
Posiadanie dodatkowej liczby całkowitej dla każdego węzła i zarządzanie nią: rozmiar poddrzewa daje O (log n) złożoność czasową . Takie zrównoważone drzewo wyszukiwania jest zwykle nazywane RankTree.
Ogólnie rzecz biorąc, istnieją rozwiązania (nie oparte na drzewie).
Pozdrowienia.
źródło
Działa to dobrze: status: to tablica przechowująca informację o znalezieniu elementu. k: to k-ty element do znalezienia. count: śledzi liczbę węzłów pokonanych podczas przechodzenia przez drzewo.
źródło
Chociaż zdecydowanie nie jest to optymalne rozwiązanie problemu, jest to kolejne potencjalne rozwiązanie, które moim zdaniem może być interesujące dla niektórych osób:
źródło
podpis:
zadzwoń jako:
definicja:
źródło
Cóż, oto moje 2 centy ...
źródło
To jest to, co myślałem i działa. Będzie działać za (log n)
źródło
Cóż, możemy po prostu użyć przemierzania kolejności i włożyć odwiedzony element na stos. pop k kilka razy, aby uzyskać odpowiedź.
możemy też zatrzymać się po k elementów
źródło
Rozwiązanie dla całego przypadku BST: -
źródło
Jądro Linuksa ma doskonałą, powiększoną czerwono-czarną strukturę danych drzewa, która obsługuje operacje oparte na rangach w O (log n) w linux / lib / rbtree.c.
Bardzo prymitywny port Java można również znaleźć pod adresem http://code.google.com/p/refolding/source/browse/trunk/core/src/main/java/it/unibo/refolding/alg/RbTree.java , razem z RbRoot.java i RbNode.java. N-ty element można uzyskać, wywołując RbNode.nth (węzeł RbNode, int n), przekazując do korzenia drzewa.
źródło
Oto zwięzła wersja w C #, która zwraca k-ty najmniejszy element, ale wymaga przekazania k jako argumentu ref (jest to to samo podejście, co @prasadvk):
To O (log n), aby znaleźć się najmniejszą węzeł, a następnie O (K) do przejścia do k-tego węzła, tak, że to O (K + log n).
źródło
http://www.geeksforgeeks.org/archives/10379
to jest dokładna odpowiedź na to pytanie: -
1. wykorzystanie przejścia w kolejności w czasie O (n) 2. użycie drzewa rozszerzonego w czasie k + log n
źródło
Nie mogłem znaleźć lepszego algorytmu… więc zdecydowałem się go napisać :) Popraw mnie, jeśli jest źle.
}
źródło
Oto kod java,
max (Node root, int k) - aby znaleźć k-t największy
min (Node root, int k) - aby znaleźć k-t najmniejszy
źródło
to też by działało. po prostu wywołaj funkcję z maxNode w drzewie
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)
źródło
Myślę, że jest to lepsze niż zaakceptowana odpowiedź, ponieważ nie trzeba modyfikować oryginalnego węzła drzewa, aby przechowywać liczbę jego węzłów podrzędnych.
Musimy tylko użyć przechodzenia w kolejności, aby policzyć najmniejszy węzeł od lewej do prawej, przestań szukać, gdy liczba będzie równa K.
źródło
Najlepsze podejście już jest, ale chciałbym dodać do tego prosty kod
}
źródło
Używanie pomocniczej klasy Result do śledzenia znalezienia węzła i bieżącego k.
źródło
Złożoność czasowa rozwiązania Pythona: O (n) Złożoność przestrzeni: O (1)
Pomysł polega na użyciu Morris Inorder Traversal
źródło
Napisałem zgrabną funkcję do obliczania k-tego najmniejszego elementu. Używam przechodzenia w kolejności i zatrzymuję się, gdy osiągnie k-ty najmniejszy element.
źródło
źródło
źródło
}
źródło