-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathzip_tree.py
196 lines (165 loc) · 6.11 KB
/
zip_tree.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# explanations for member functions are provided in requirements.py
# each file that uses a Zip Tree should import it from this file.
from random import random
from typing import TypeVar
KeyType = TypeVar("KeyType")
ValType = TypeVar("ValType")
class Node:
def __init__(self, key: KeyType, val: ValType, rank: int, left=None, right=None):
self.key = key
self.val = val
self.rank = rank
self.left = left
self.right = right
def __str__(self):
return f"Node({self.key}, {self.val}, {self.rank})"
class ZipTree:
def __init__(self):
self.root = None
self.size = 0
@staticmethod
def get_random_rank() -> int:
# https://stats.stackexchange.com/a/487939
rank = 0
while True:
if random() < 0.5:
return rank
rank += 1
# Method for inserting new node with key
# i) Find node to be replaced by new node depending on key and sampled rank
# ii) Unzipping of subtree
def insert(self, key: KeyType, val: ValType, rank: int = -1):
self.size += 1
new_node = Node(key, val, rank if rank != -1 else self.get_random_rank())
# current: for storing the node to be replaced
# previous: for storing the parent of current
current = self.root
previous = None
# find node to be replaced
while current is not None and (
new_node.rank < current.rank
or (new_node.rank == current.rank and new_node.key > current.key)
):
previous = current
if new_node.key < current.key:
current = current.left
else:
current = current.right
# insert new node
if current == self.root:
self.root = new_node
elif new_node.key < previous.key:
previous.left = new_node
else:
previous.right = new_node
# preserving replaced node
if current is None:
return
if new_node.key < current.key:
new_node.right = current
else:
new_node.left = current
previous = new_node
# Unzip
# current: moves along search path, stops temporarily when element changes relation (smaller or greater) to inserted key
# previous: last element with different relation to inserted element than "current"
# temp: starts at inserted element, changes to "previous" whenever "previous" changes; place for "current" to be inserted when it stops moving
temp = None
while current is not None:
temp = previous
if current.key < new_node.key:
while current is not None and current.key < new_node.key:
previous = current
current = current.right
else:
while current is not None and current.key > new_node.key:
previous = current
current = current.left
if temp.key > new_node.key or (
temp == new_node and previous.key > new_node.key
):
temp.left = current
else:
temp.right = current
# Method for deletion of certain key
# i) Find key in tree
# ii) Replace node with a child, depending on rank
# iii) Zipping of subtrees of deleted node
def remove(self, key: KeyType):
self.size -= 1
# current: at first node to be deleted; then replacing node
# previous: parent of "current"
# left: left child of "current", first part for zipping
# right: right child of "current", second part for zipping
current = self.root
previous = None
left = None
right = None
# find key
while key != current.key:
previous = current
if key < current.key:
current = current.left
else:
current = current.right
left = current.left
right = current.right
# 0 or 1 child: child becomes replacing node
if left is None:
current = right
elif right is None:
current = left
# 2 children: child with higher rank (or lower key in case of a tie) becomes replacing node
elif left.rank >= right.rank:
current = left
else:
current = right
# replace node
if self.root.key == key:
self.root = current
elif key < previous.key:
previous.left = current
else:
previous.right = current
# Zip
# previous: builds zipped path depending on rank of the elements
while left is not None and right is not None:
if left.rank >= right.rank:
while left is not None and left.rank >= right.rank:
previous = left
left = left.right
previous.right = right
else:
while right is not None and left.rank < right.rank:
previous = right
right = right.left
previous.left = left
def find(self, key: KeyType) -> ValType:
current = self.root
while key != current.key:
if key < current.key:
current = current.left
else:
current = current.right
return current.val
def get_size(self) -> int:
return self.size
def get_height(self) -> int:
def height(node):
if node is None:
return 0
return 1 + max(height(node.left), height(node.right))
return height(self.root) - 1
def get_depth(self, key: KeyType):
current = self.root
depth = 0
while key != current.key:
if key < current.key:
current = current.left
else:
current = current.right
depth += 1
return depth
# feel free to define new classes/methods in addition to the above
# fill in the definitions of each required member function (above),
# and any additional member functions you define