https://en.wikipedia.org/wiki/Day%E2%80%93Stout%E2%80%93Warren_algorithm
Algorytm DSW (Day, Stout, Warren) służy do zrównoważenia drzewa BST. Algorytm działa w miejscu i w czasie O(n).
W oryginalnym algorytmie DSW wprowadza się pseudo-root, którego prawym dzieckiem jest zwykły root. Chyba chodzi o to, żeby nie robić rotacji pseudo-root, tylko poniżej, więc pseudo-root jest zawsze rodzicem poddrzewa. Przy równoważeniu drzewa ciągle trzeba osobno traktować root, bo on nie ma rodzica.
Pełne drzewa binarne z m wierzchołkami. m=1, liście 1, wysokość h=1 m=3=2^2-1, liście 2, wysokość h=2 m=7=2^3-1, liście 4=2^2, wysokość h=3 m=15=2^4-1, liście 8=2^3, wysokość h=4 m=31=2^5-1, liście 16=2^4, wysokość h=5 m=2^h-1, liście 2^(h-1), wysokość h Przejście do nowego poziomu: m = 2*m+1. h = log2(m+1), ale ogólnie dla n będzie wystawać parę liści h = |_log2(n+1)_| h = math.floor(math.log(n+1, 2)) # Py m = int(math.pow(2, math.floor(math.log(n+1, 2)))) -1 # Py2, Py3 m = int(math.pow(2, math.floor(math.log2(n+1)))) -1 # Py3.3+ better
| Przykład drzewa z wierzchołkami od 1 do 10. Mniejsze pełne drzewo ma m=7. | Po fazie I mamy faktycznie listę jednokierunkową o długości n=10. | Robimy wstępne n-m rotacji, żeby została wysokość drzewa m=7. | (1) rot1 2 rot2 2 rot3 (2) | \ / \ / \ / \ | 2 1 (3) 1 4 1 4 | \ \ / \ / \ | 3 4 3 (5) 3 6 | \ \ \ / \ | 4 5 6 5 7 | \ \ \ \ | 5... 6... 7... 8...
| Pierwsza pętla z m=3. | 4 4 (4) | / \ / \ / \ | 2 (6) 2 7 2 7 | / \ / \ / \ / \ / \ / \ | 1 3 5 7 1 3 6 (8) 1 3 6 9 | \ / \ / / \ | 8... 5 9... 5 8 10
| Druga pętla z m=1. | 7 | / \ | 4 9 | / \ / \ | 2 6 8 10 | / \ / | 1 3 5
# Wersja wg Wikipedii z pseudo_root. def dsw(top): if top is None: return # Faza I. Tworzenie kręgosłupa. Równolegle zliczamy węzły. pseudo_root = Node() pseudo_root.right = top n = tree_to_vine(pseudo_root) # Faza II. Równoważenie drzewa. vine_to_tree(pseudo_root, n) return pseudo_root.right
def tree_to_vine(pseudo_root): parent = pseudo_root node = pseudo_root.right n = 0 # jednocześnie zliczamy węzły drzewa while node: while node.left: node = rotate_right(node) parent.right = node # zmienił się wierzchołek poddrzewa parent = node node = node.right n += 1 return n
def vine_to_tree(pseudo_root, n): # Obliczenie liczby węzłow najbliższego pełnego drzewa binarnego. m = 1 # drzewo pełne z jednym wierzchołkiem while m <= n: m = m * 2 + 1 # dodajemy kolejne poziomy drzewa # Teraz m jest większe od n, nawet gdy n jest pełnym drzewem. m = (m - 1) // 2 # za dużo o jeden poziom #m = int(math.pow(2, math.floor(math.log2(n+1)))) -1 # Py3.3+ # Potrzebujemy n-m rotacji co drugi wierzchołek z prawej strony. # Na najniższym poziomie drzewa będzie n-m liści. compress(pseudo_root, n-m) while m > 1: m = m // 2 compress(pseudo_root, m) # m to obecna wysokość drzewa
def compress(pseudo_root, counter): parent = pseudo_root for i in range(counter): parent.right = rotate_left(parent.right) parent = parent.right