Rozważ nieoznakowane, ukorzenione drzewa binarne. Możemy skompresować takich drzew: gdy istnieją wskaźniki do poddrzew i T ' z T = T ' (ustne = jak równość strukturalne), możemy zapisać (wlog) T i zastąpić wszystkie wskaźniki do T ' z wskazówki dla T . Zobacz odpowiedź uli na przykład.
Podaj algorytm, który przyjmuje drzewo w powyższym sensie jako dane wejściowe i oblicza (minimalną) liczbę węzłów pozostałych po kompresji. Algorytm powinien działać w czasie (w modelu kosztu jednolitego) z n liczbą węzłów na wejściu.
To było pytanie egzaminacyjne i nie byłem w stanie znaleźć dobrego rozwiązania, ani go nie widziałem.
algorithms
data-structures
trees
binary-trees
Raphael
źródło
źródło
Odpowiedzi:
Tak, możesz wykonać tę kompresję w czasie , ale nie jest to łatwe :) Najpierw dokonamy pewnych obserwacji, a następnie przedstawimy algorytm. Zakładamy, że drzewo początkowo nie jest skompresowane - nie jest to tak naprawdę potrzebne, ale ułatwia analizę.O(nlogn)
Po pierwsze, charakteryzujemy indukcyjnie „równość strukturalną”. Niech i T ' będą dwoma (pod) drzewami. Jeśli zarówno T, jak i T ' są zerowymi drzewami (w ogóle nie mają wierzchołków), są one strukturalnie równoważne. Jeśli oba T i T ' nie są zerowymi drzewami, to są one strukturalnie równoważne, jeśli ich lewe dzieci są strukturalnie równoważne, a ich prawe dzieci są strukturalnie równoważne. „Równoważność strukturalna” to minimalny stały punkt nad tymi definicjami.T T′ T T′ T T′
Na przykład dowolne dwa węzły liścia są strukturalnie równoważne, ponieważ oba mają drzewa zerowe jako oba swoje dzieci, które są strukturalnie równoważne.
Ponieważ denerwujące jest stwierdzenie „ich lewe dzieci są strukturalnie równoważne, podobnie jak ich prawe dzieci”, często mówimy „ich dzieci są strukturalnie równoważne” i zamierzamy to samo. Zauważ też, że czasami mówimy „ten wierzchołek”, gdy mamy na myśli „poddrzew zakorzeniony w tym wierzchołku”.
Najpierw kompresujemy wszystkie liście (możemy je znaleźć na liście z wierzchołkami o głębokości 0) w jednym wierzchołku. Przypisujemy temu wierzchołkowi identyfikator. Kompresja dwóch wierzchołków odbywa się poprzez przekierowanie elementu nadrzędnego jednego z wierzchołków, tak aby wskazywał na drugi wierzchołek.
Iterujemy wszystkie listy z węzłami o jednakowej głębokości od małej głębokości do dużej głębokości. Dla każdego poziomu tworzymy listę par liczb całkowitych, gdzie każda para odpowiada identyfikatorom potomków niektórych wierzchołków na tym poziomie. Mamy dwa wierzchołki na tym poziomie, które są strukturalnie równoważne, jeśli ich odpowiadające sobie pary liczb całkowitych są równe. Korzystając z porządku leksykograficznego, możemy je posortować i uzyskać zestawy równych par liczb całkowitych. Kompresujemy te zestawy do pojedynczych wierzchołków jak wyżej i nadamy im identyfikatory.
źródło
degree
” stopnia prawdopodobnie powinny byćdepth
? I mimo, że drzewa CS rosną w dół, „wysokość drzewa” jest mniej myląca niż „głębokość drzewa”.Kompresowanie niemodyfikowalnej struktury danych, aby nie powielała żadnego strukturalnie równego podtermu, znane jest jako sumowanie skrótów . Jest to ważna technika zarządzania pamięcią w programowaniu funkcjonalnym. Hash Consing to rodzaj systematycznego zapamiętywania struktur danych.
Rozważę drzewa jako mające następującą strukturę (zapisaną tutaj w składni Haskell):
Możemy wykorzystać strukturę danych w czasie logarytmicznym
nodes
, na przykład zrównoważone drzewo wyszukiwania binarnego. Poniżej wywołamlookup nodes
operację, która wyszukuje klucz wnodes
strukturze danych, orazinsert nodes
operację, która dodaje wartość do nowego klucza i zwraca ten klucz.Teraz przechodzimy przez drzewo i dodajemy węzły. Chociaż piszę w pseudokodzie podobnym do Haskella, będę traktować
nodes
jako zmienną globalną zmienną; będziemy tylko do niego dodawać, ale wstawki muszą być w całości gwintowane.add
Funkcja recurses na drzewie, dodając do jego poddrzewa nanodes
mapie i zwraca identyfikator korzenia.Liczba
insert
wywołań, która jest również ostatecznym rozmiaremnodes
struktury danych, to liczba węzłów po maksymalnej kompresji. (W razie potrzeby dodaj jedno puste drzewo).źródło
nodes
insert
iadd
powinno być wyraźnie określone, i należy nadać funkcję, która faktycznie rozwiązuje problem, imho.nodes
to zmienny zmienną dla wygody, ale można wkręcić ją na wskroś. Nie zamierzam podawać pełnego kodu, to nie jest SO.Oto kolejny pomysł, który ma na celu (iniekcyjne) kodowanie struktury drzew w liczbach, a nie tylko ich arbitralne oznaczanie. W tym celu wykorzystujemy fakt, że faktoryzacja pierwsza dowolnej liczby jest wyjątkowa.
To ostatnie założenie dotyczy różnych maszyn; w takim przypadku wolałoby się zastosować coś podobnego do funkcji parowania Cantora zamiast potęgowania.
źródło
Ponieważ zdjęcia nie są dozwolone w komentarzach:
w lewym górnym rogu: drzewo wejściowe
u góry po prawej: poddrzewa zakorzenione w węzłach 5 i 7 są również izomorficzne.
dolny lewy i prawy: skompresowane drzewa nie są jednoznacznie zdefiniowane.
źródło
Edycja: Czytam pytanie, ponieważ T i T 'były dziećmi tego samego rodzica. Użyłem również definicji kompresji jako rekurencyjnej, co oznacza, że można skompresować dwa wcześniej poddane kompresji poddrzewa. Jeśli to nie jest prawdziwe pytanie, moja odpowiedź może nie działać.
Gdzie
hasSameStructure()
jest funkcja, która porównuje dwa już skompresowane poddrzewa w czasie liniowym, aby sprawdzić, czy mają dokładnie taką samą strukturę. Pisanie liniowej funkcji rekurencyjnej w czasie, która przechodzi przez każdą i sprawdza, czy jedno poddrzewo ma pozostawione dziecko za każdym razem, gdy robi to drugie itd., Nie powinno być trudne.źródło