簡體   English   中英

實現一個僅在葉子中存儲值的 AVL 平衡搜索樹字典

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM