[英]Implement an AVL-balanced search tree dictionary that only stores values in leaves
學校有一個任務:實現一個基於AVL平衡搜索樹的字典,數據存儲在樹葉中。
我寫了一本字典 class:一棵 AVL 平衡搜索樹,其中的值存儲在節點中。 我怎樣才能正確地更正它,以便值只存儲在葉子中? 我將不勝感激任何幫助。
class Node:
def __init__(self, key, value=None, left=None, right=None, inv=False):
if inv:
left, right = right, left
self.key = key
self.value = value
self.left = left
self.right = right
self.exist = 1
if left is not None and right is not None:
self.height = 1 + max(self.left.height, self.right.height)
elif left is not None:
self.height = 1 + self.left.height
elif right is not None:
self.height = 1 + self.right.height
else:
self.height = 1
class Dict:
def __init__(self):
self.root = None
self.len = 0
def __len__(self):
return self.len
def __getitem__(self, key):
if not self.root:
raise KeyError()
else:
return self.get(self.root, key)
def __contains__(self, key):
try:
self.__getitem__(self.root, key)
return True
except:
return False
def __setitem__(self, key, value):
if not self.root:
self.root = Node(key, value)
else:
self.root = self.put(self.root, key, value)
self.len += 1
def __delitem__(self, key):
if key not in self:
raise KeyError()
else:
self.delete(self.root, key)
self.len -= 1
def delete(self, tree, key):
if key == tree.key:
tree.exist = 0
return
return self.delete(self.children(tree, key < tree.key)[1], key)
def height(self, tree): return 0 if tree is None else tree.height
def children(self, tree, inv): return (tree.right, tree.left) if inv else (tree.left, tree.right)
def reassoc(self, tree, inv):
l, r = self.children(tree, inv)
rl, rr = self.children(r, inv)
return Node(r.key, r.value, Node(tree.key, tree.value, l, rl, inv), rr, inv)
def avl(self, tree, inv):
l, r = self.children(tree, inv)
if self.height(r) - self.height(l) < 2:
return tree
rl, rr = self.children(r, inv)
if self.height(rl) - self.height(rr) == 1:
r = self.reassoc(r, not inv)
return self.reassoc(Node(tree.key, tree.value, l, r, inv), inv)
def put(self, tree, key, value):
if tree is None:
return Node(key, value, None, None)
if tree.key == key:
self.len -= 1
if tree.exist==0:self.len+=1
return Node(key, value, tree.left, tree.right)
inv = key < tree.key
left, right = self.children(tree, inv)
return self.avl(Node(tree.key, tree.value, left,
self.put(right, key, value), inv), inv)
def get(self, tree, key):
if tree is None:
raise KeyError()
if key == tree.key and tree.exist == 0:
raise KeyError()
elif key == tree.key and tree.exist != 0:
return tree.value
return self.get(self.children(tree, key < tree.key)[1], key)
def print_tree(tree, indent = 0):
if tree == None: print()
print(' '*indent + str(tree.key) + ' -> ' + str(tree.value))
if tree.left: print_tree(tree.left, indent + 2)
if tree.right: print_tree(tree.right, indent + 2)
t = Dict()
for v, k in enumerate([5,7,2,1,3,6,2,7]):
t.__setitem__(k, v)
print_tree(t.root)
print()
要在葉子中獲取值,您首先要找到應該進行插入的葉子節點,然后將其替換為具有兩個子節點的無值父節點:原始葉子節點和新節點。
這意味着樹將永遠是滿的,即所有內部節點都有兩個孩子,從來沒有一個。 旋轉——當需要重新平衡樹時——不會影響這個屬性。
然而,查找鍵的值存在一點挑戰,因為現在可以在內部節點中找到鍵,並且可能不清楚應該在哪個子樹中找到具有該鍵的葉子。 盡管插入算法最初可以同意始終將這樣的葉子放在左子樹中,但旋轉不會保持此屬性,因此通常目標葉子可以在任一子樹中。
有幾種方法可以解決這個問題。 一種簡單的方法是向樹中添加一個字典,將每個鍵映射到一個節點。 這將使__contains__
、 __getitem__
和__delitem__
等幾種方法非常有效,因為它們實際上不需要遍歷樹。 但如果這不是一個可接受的解決方案,那么重用內部節點的value
屬性也許是可以接受的,因為它們不再使用該屬性。 他們可以引用具有相同鍵的葉節點,而不是持有一個值。 為此,葉節點永遠不會被丟棄(因為被同一鍵的新葉所取代)而是用新值更新是很重要的。
下面是實現這個想法的put
實現:
def put(self, tree, key, value):
# Make a leaf node the base case for the recursion pattern
if not tree.left: # Is leaf? (Note that each node has 2 or no children)
if key == tree.key:
self.len -= tree.exist
# Mutate leaf so references to it remain valid
tree.exist = 1
tree.value = value
return tree
# Otherwise this leaf node becomes an internal node:
# Set its value attribute to reference the newly created leaf
elif key < tree.key:
return Node(tree.key, tree, Node(key, value), tree)
else:
return Node(tree.key, tree, tree, Node(key, value))
if tree.key == key: # Key matches internal node's key
# Follow the internal node's reference to the leaf having the same key
self.put(tree.value, key, value)
return tree
inv = key < tree.key
left, right = self.children(tree, inv)
return self.avl(Node(tree.key, tree.value, left,
self.put(right, key, value), inv), inv)
同樣,需要更新get
以便它使用該內部引用:
def get(self, tree, key):
if not tree.left: # It is a leaf
if key != tree.key or not tree.exist:
raise KeyError()
return tree.value
if key == tree.key:
# Follow the internal node's reference to leaf
return self.get(tree.value, key)
return self.get(self.children(tree, key < tree.key)[1], key)
並且您會希望避免內部節點的值屬性由print_tree
function 打印。我個人更喜歡中序遍歷,以便 output 看起來像一個 90° 旋轉的樹:
def print_tree(tree, indent = ""):
if tree.right:
print_tree(tree.right, indent + " ")
if tree.left == tree.right: # Is leaf
print(f"{indent}{tree.key} -> {tree.value}")
else: # Is internal (then don't print value attribute)
print(f"{indent}{tree.key}")
if tree.value is None:
raise ValueError("unexpected None value")
if tree.left:
print_tree(tree.left, indent + " ")
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.