BIT: używając binarnego drzewa indeksowanego? [zamknięte]

Binarne drzewo indeksowane ma niewiele lub stosunkowo nie ma teorii do zbadania w porównaniu z innymi strukturami danych. Jedynym miejscem, gdzie jest nauczany zwięźle jest TopCoder tutorial . Chociaż samouczek jest kompletny we wszystkich wyjaśnieniach, nie mogę zrozumieć, że jaka jest intuicja za takim drzewem? I jak udowodnić swoją poprawność?

Zakładam, że dowód jest skomplikowany do wyjaśnienia. Więc kiedy używasz bitu, jakie podejście stosujesz?

Author: James Z, 2013-03-15

1 answers

Znalazłem ta odpowiedź autorstwa @ templatetypedef bardzo jasno wyjaśnia intuicję i dowód binarnego drzewa indeksowanego: Odpowiedź....

Intuicyjnie można myśleć o binarnym drzewie indeksowanym jako skompresowanej reprezentacji drzewa binarnego, która sama w sobie jest optymalizacją standardowej reprezentacji tablicy. Ta odpowiedź idzie w jednym możliwym wyprowadzeniu.

Załóżmy, na przykład, że chcesz przechowywać skumulowane częstotliwości dla łącznie 7 różnych żywioły. Możesz zacząć od wypisania siedmiu wiader, do których będą rozdysponowane liczby:

[   ] [   ] [   ] [   ] [   ] [   ] [   ]
  1     2     3     4     5     6     7

Załóżmy teraz, że kumulatywne częstotliwości wyglądają mniej więcej tak:

[ 5 ] [ 6 ] [14 ] [25 ] [77 ] [105] [105]
  1     2     3     4     5     6     7

Używając tej wersji tablicy, możesz zwiększyć skumulowaną częstotliwość dowolnego elementu, zwiększając wartość liczby przechowywanej w tym miejscu, a następnie zwiększając częstotliwości wszystkiego, co nadejdzie później. Na przykład, aby zwiększyć łączną częstotliwość 3 po 7 możemy dodać 7 do każdego elementu tablicy na lub po pozycji 3, Jak pokazano tutaj:

[ 5 ] [ 6 ] [21 ] [32 ] [84 ] [112] [112]
  1     2     3     4     5     6     7

Problem polega na tym, że zajmuje to O (n) czasu, co jest dość powolne, jeśli n jest duże.

Jednym ze sposobów, w jaki możemy myśleć o ulepszeniu tej operacji, byłaby zmiana tego, co przechowujemy w wiadrach. Zamiast zapisywać skumulowaną częstotliwość do danego punktu, można zamiast tego pomyśleć o przechowywaniu tylko kwoty, którą bieżąca częstotliwość wzrosła w stosunku do poprzednie wiadro. Na przykład, w naszym przypadku, przepiszemy powyższe wiadra w następujący sposób:

Before:
[ 5 ] [ 6 ] [21 ] [32 ] [84 ] [112] [112]
  1     2     3     4     5     6     7

After:
[ +5] [ +1] [+15] [+11] [+52] [+28] [ +0]
  1     2     3     4     5     6     7

Teraz możemy zwiększyć częstotliwość w wiadrze w czasie O (1), dodając odpowiednią ilość do tego wiadra. Jednak całkowity koszt przeprowadzenia wyszukiwania staje się teraz O( n), ponieważ musimy ponownie obliczyć sumę w wiadrze, sumując wartości we wszystkich mniejszych wiadrach.

Pierwszy duży wgląd musimy dostać się stąd do binarnego drzewa indeksowane jest następująca: zamiast przeliczać w sposób ciągły sumę elementów tablicy poprzedzających dany element, co by było, gdybyśmy wstępnie obliczyli sumę wszystkich elementów przed określonymi punktami w sekwencji? Gdybyśmy mogli to zrobić, to moglibyśmy obliczyć sumę skumulowaną w pewnym punkcie, po prostu sumując właściwą kombinację tych wstępnie obliczonych Sum.

Jednym ze sposobów na to jest zmiana reprezentacji z tablicy wiadrów na binarne drzewo węzłów. Każdy węzeł zostanie opatrzony adnotacją o wartości, która reprezentuje skumulowaną sumę wszystkich węzłów po lewej stronie danego węzła. Na przykład, załóżmy, że z tych węzłów zbudujemy następujące drzewo binarne:

             4
          /     \
         2       6
        / \     / \
       1   3   5   7

Teraz możemy powiększyć każdy węzeł, przechowując skumulowaną sumę wszystkich wartości łącznie z tym węzłem i jego lewym poddanym. Na przykład, biorąc pod uwagę nasze wartości, przechowujemy następujące wartości:

Before:
[ +5] [ +1] [+15] [+11] [+52] [+28] [ +0]
  1     2     3     4     5     6     7

After:
                 4
               [+32]
              /     \
           2           6
         [ +6]       [+80]
         /   \       /   \
        1     3     5     7
      [ +5] [+15] [+52] [ +0]

Biorąc pod uwagę tę strukturę drzewa, łatwo jest określić sumę kumulacyjną do punkt. Idea jest następująca: utrzymujemy licznik, początkowo 0, a następnie wykonujemy normalne wyszukiwanie binarne aż do znalezienia danego węzła. Jak to robimy, mamy również następujące: za każdym razem, że poruszamy się w prawo, dodajemy również w bieżącej wartości do licznika.

Na przykład, załóżmy, że chcemy sprawdzić sumę dla 3. W tym celu wykonujemy następujące czynności:]}

  • zacznij od korzenia (4). Licznik wynosi 0.
  • idź w lewo do node (2). Licznik wynosi 0.
  • przejdź w prawo do węzła (3). Licznik wynosi 0 + 6 = 6.
  • Znajdź węzeł (3). Licznik wynosi 6 + 15 = 21.

Możesz sobie wyobrazić również uruchamianie tego procesu w odwrotnej kolejności: zaczynając od danego węzła, inicjalizuj licznik do wartości tego węzła, a następnie idź w górę drzewa do korzenia. Za każdym razem, gdy podążasz za prawym łączem potomnym w górę, Dodaj wartość w węźle, do którego docierasz. Na przykład, aby znaleźć częstotliwość dla 3, możemy wykonać następujące czynności:

  • zacznij od węzła (3). Licznik to 15.
  • idź w górę do węzeł (2). Licznik wynosi 15 + 6 = 21.
  • idź w górę do węzła (1). Licznik 21.

Aby zwiększyć częstotliwość węzła (i, domyślnie, częstotliwości wszystkich węzłów, które przychodzą po nim), musimy zaktualizować zestaw węzłów w drzewie, które zawierają ten węzeł w jego lewym poddrzewie. Aby to zrobić, wykonujemy następujące czynności: zwiększamy częstotliwość dla tego węzła, a następnie zaczynamy podchodzić do korzenia drzewa. Za każdym razem, gdy klikniesz link, który zabierze cię w górę jako lewe dziecko, zwiększ częstotliwość napotkanego węzła poprzez dodanie bieżącej wartości.

Na przykład, aby zwiększyć częstotliwość węzła 1 o pięć, wykonamy następujące czynności:]}
                 4
               [+32]
              /     \
           2           6
         [ +6]       [+80]
         /   \       /   \
      > 1     3     5     7
      [ +5] [+15] [+52] [ +0]

Począwszy od węzła 1, zwiększ jego częstotliwość o 5, aby uzyskać

                 4
               [+32]
              /     \
           2           6
         [ +6]       [+80]
         /   \       /   \
      > 1     3     5     7
      [+10] [+15] [+52] [ +0]

Teraz idź do rodzica:

                 4
               [+32]
              /     \
         > 2           6
         [ +6]       [+80]
         /   \       /   \
        1     3     5     7
      [+10] [+15] [+52] [ +0]

Podążaliśmy za lewym łączem potomnym w górę, więc zwiększamy również częstotliwość tego węzła:

                 4
               [+32]
              /     \
         > 2           6
         [+11]       [+80]
         /   \       /   \
        1     3     5     7
      [+10] [+15] [+52] [ +0]

Przechodzimy teraz do jego rodzica:

               > 4
               [+32]
              /     \
           2           6
         [+11]       [+80]
         /   \       /   \
        1     3     5     7
      [+10] [+15] [+52] [ +0]
/ Align = "left" / Linear zwiększ również ten węzeł:
                 4
               [+37]
              /     \
           2           6
         [+11]       [+80]
         /   \       /   \
        1     3     5     7
      [+10] [+15] [+52] [ +0]

A teraz skończyliśmy!

Ostatnim krokiem jest konwersja z tego do binarnego drzewa indeksowanego, i to jest miejsce, gdzie możemy zrobić kilka zabawnych rzeczy z liczbami binarnymi. Przepisz każdy indeks wiadra w tym drzewie w postaci binarnej:

                100
               [+37]
              /     \
          010         110
         [+11]       [+80]
         /   \       /   \
       001   011   101   111
      [+10] [+15] [+52] [ +0]

Tutaj możemy zrobić bardzo, bardzo fajną obserwację. Weź jedną z tych liczb binarnych i znajdź ostatnią 1, która została ustawiona w liczbie, a następnie upuść ten bit, wraz ze wszystkimi bitami, które przychodzą po nim. Teraz pozostało Ci:

              (empty)
               [+37]
              /     \
           0           1
         [+11]       [+80]
         /   \       /   \
        00   01     10   11
      [+10] [+15] [+52] [ +0]

Oto naprawdę, naprawdę fajna obserwacja: jeśli traktujesz 0 jako "lewy", a 1 jako" prawy", pozostałe bity na każdej liczbie określają dokładnie, jak zacząć od początku, a następnie przejść do tej liczby. Na przykład node 5 ma binarny wzór 101. Ostatni 1 jest ostatnim bitem, więc rzucamy go, aby uzyskać 10. Rzeczywiście, jeśli zaczniesz od głównego, idź w prawo (1), a następnie w lewo (0), skończysz na węźle 5!

Powód, dla którego jest to istotne jest to, że nasze operacje wyszukiwania i aktualizacji zależą od ścieżki dostępu od węzła z powrotem do katalogu głównego oraz od tego, czy podążamy za lewym czy prawym łączem potomnym. Na przykład, podczas wyszukiwania, po prostu dbamy o lewe linki, które śledzimy. Podczas aktualizacji dbamy tylko o odpowiednie linki, które śledzimy. To binarne drzewo indeksowane robi to wszystko bardzo wydajnie, używając tylko bitów w indeksie.

Kluczem jest następująca własność tego doskonałego binarnego drzewo:

Dany węzeł n, następny węzeł na ścieżce dostępu z powrotem do katalogu głównego, w którym idziemy w prawo, jest podawany przez pobranie binarnej reprezentacji n i usunięcie ostatniego 1.

Na przykład spójrz na ścieżkę dostępu dla węzła 7, czyli 111. Węzły na ścieżce dostępu do korzenia, które bierzemy, które wymagają podążania prawym wskaźnikiem w górę, to

  • Node 7: 111
  • Node 6: 110
  • Node 4: 100

Wszystkie te są właściwe linki. Jeśli weźmiemy ścieżkę dostępu do węzła 3, czyli 011, i spojrzymy na węzły, w których idziemy w prawo, otrzymamy

  • Node 3: 011
  • Node 2: 010
  • (W związku z tym, że nie jest to możliwe, nie jest to możliwe.]}

Oznacza to, że możemy bardzo, bardzo efektywnie obliczyć sumę skumulowaną do węzła w następujący sposób:

  • wypisuje węzeł N w postaci binarnej.
  • Ustaw licznik na 0.
  • powtórz następujący while N ≠ 0:
    • Dodaj wartość w node n.
    • Usuń lewy 1 bit Z n.

Podobnie, zastanówmy się, jak zrobilibyśmy krok aktualizacji. Aby to zrobić, chcielibyśmy podążać ścieżką dostępu z powrotem do katalogu głównego, aktualizując wszystkie węzły, w których podążaliśmy lewym łączem w górę. Możemy to zrobić zasadniczo wykonując powyższy algorytm, ale przełączając wszystkie 1 na 0 i 0 Na 1.

Ostatnim krokiem w drzewie binarnym jest zwrócenie uwagi, że ponieważ z tej bitowej sztuczki, nawet nie musimy mieć drzewo przechowywane jawnie już. Możemy po prostu zapisać wszystkie węzły w tablicy o długości n, a następnie użyć technik bitwise twidling do nawigowania po drzewie bezwarunkowo. W rzeczywistości to właśnie robi bitowo indeksowane drzewo-przechowuje węzły w tablicy , a następnie wykorzystuje te bitowo sztuczki, aby skutecznie symulować chodzenie w górę tego drzewa.

Mam nadzieję, że to pomoże!

 76
Author: Nikunj Banka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-13 12:48:30