BST删除方法运行不正常

BST delete method not functioning well

提问人:Muhammad Olamide 提问时间:11/7/2023 更新时间:11/7/2023 访问量:17

问:

所以我对二叉搜索树很不满意,现在即使是人工智能也无法帮助解决这个特定问题,我想这是一个逻辑遗漏的问题。删除方法无法正常工作,我需要帮助。tree.delete(2) 应该删除 2 和 2 应该在末尾打印,但它仍然打印 2。我会喜欢提供的任何帮助。


class BstNode:
    def __init__(self, val=None) :
        self.val = val
        self.left = None
        self.right = None

    def insert(self, val):
        if   self.val == None:
            self.val = val
        elif self.val == val:
            print("Duplicate Value") 
            return
        elif val > self.val:
            if self.right == None:
                self.right = BstNode(val)
            else:
                self.right.insert(val)
        elif val < self.val:
            if self.left == None:
                self.left = BstNode(val)
            else:
                self.left.insert(val)

        # the init_call values are for the print just to debug, has no uses after this to print full list after all recursive call
      
    def preorder(self, visited=None, init_call=True):
        if visited is None:
            visited = []
        visited.append(self.val)
        if self.left:
            self.left.preorder(visited, False)
        if self.right:
            self.right.preorder(visited, False)
        if init_call:  # Check if this is the initial call to the function
            print(visited)
        return visited

    def postorder(self, visited=None, init_call=True):
        if visited is None:
            visited = []
        if self.left:
            self.left.postorder(visited, False)
        if self.right:
            self.right.postorder(visited, False)
        visited.append(self.val)
        if init_call:  # Check if this is the initial call to the function
            print(visited)
        return visited

    # in order from lowest to highest
    def inorder(self, visited=None, init_call=True):
        if visited is None:
            visited = []
        if self.left:
            self.left.inorder(visited, False)
        visited.append(self.val)
        if self.right:
            self.right.inorder(visited, False)
        if init_call:  # Check if this is the initial call to the function
            print(visited)
        return visited
    
        
    def print(self):
        if self.left:
            self.left.print()
        if self.val !=None:
            print(self.val)
        if self.right:
            self.right.print()
    def get_min(self):
        if self.left is None:
            print(self.val)
            return self.val
        return self.left.get_min()
    def get_max(self):
        if self.right is None:
            print (self.val)
            return self.val
        return self.right.get_max()
    def exist(self, val):
        if self.val == val:
            return True
        if self.right and val > self.val:
            return self.right.exist(val)
        elif self.left and val < self.val:
            return self.left.exist(val)
        return False
    def height(self):
        if self.val is None:
            return 0
        left_height = 0
        right_height = 0
        if self.left:
           left_height= self.left.height()
        if self.right:
           right_height= self.right.height()
        return 1 + max(left_height, right_height)
    
    def delete(self, val):
        if self.val is None:
            return self
        elif val < self.val:
            if self.left:
                self.left = self.left.delete(val)
        elif val > self.val:
            if self.right:
                self.right = self.right.delete(val)
        else:
            
            
            if self.right is None:
                temp = self.left
                del self 
                return temp
            elif self.left is None:   
                temp =  self.right
                del self 
                return temp
            else:
                temp = self.right.findMin()
                self.val = temp.val
                self.right = self.right.delete(temp.val)
            
        return self


         
       
    def findMin(self):
        current = self
        while current.left is not None:
            current = current.left
        return current




def fill_tree(tree, num_range = 10, max_int = 100):
    from random import randint
    tree.insert(35)
    for _ in range( num_range):
        
        cur_element = randint(0, max_int)
        tree.insert(cur_element)
    return tree


tree = BstNode()
tree.insert(2)
tree.insert(5)
tree.insert(7)
tree.insert(10)
tree.insert(20)
tree.insert(35)
print("_________PRINT________________")
tree.print()
print("_________PREORDER________________")
tree.preorder()
print("_________POSTORDER________________")
tree.postorder()
print("_________INORDER________________")
tree.inorder()
print("_________GETMIN________________")
tree.get_min()
print("_________GETMAX________________")
tree.get_max()
print("_________EXISIT________________")
print(tree.exist(35))
print("_________HEIGHT________________")
print(tree.height())
tree.delete(2)
tree.delete(5)
tree.delete(7)
tree.delete(10)
tree.delete(20)
tree.delete(35)
print("_________PRINT________________")
tree.print()

            


2-3 天后,我设法理解并编译了这段代码

打印 shhould 在末尾不打印任何内容,但仍然打印两个,表明打印在两个上不起作用,因为 2 是应该在左侧末尾的最低值。我将不胜感激所提供的所有帮助。

python 二进制搜索树

评论

1赞 trincot 11/7/2023
这不是你的问题,但乍一看,你似乎认为一棵树与一个节点相同,给人一种不舒服的情况,即使树是空的,你也必须有一个节点,这给了这个值一些神奇的含义,可以说虽然这是一个节点,但它并不是一个真正的节点。糟糕的设计。这会导致一开始为 True,但当您向树中添加值时,它会返回 False。这只是众多问题之一。使用两个类。None.exists(None)

答:

0赞 trincot 11/7/2023 #1

问题在于,当您的函数在第一次调用时检测到(根)节点具有要删除的值(即 2),它会返回一个子节点引用,但返回的引用“无处可去”:您的主程序不查看返回的内容,而只是期望工作完成。此外,没有做任何有用的事情,至少对树没有。那只是解除了名称的束缚。所以什么都没有改变 -- 没有删除任何节点。deletedeletedel self

一个快速的解决方法是在你拥有它的两个地方都用替换。这至少“清除”了节点,使其处于“僵尸”状态。如果它恰好是根节点,则将被视为已删除,即使它仍然是对节点的引用。del selfself.val = Nonetree

尽管这解决了您眼前的问题,但使用相同的类来表示树和节点是一个糟糕的设计选择。结果是,你需要把这个“无效”节点视为一个神奇的值,因为你没有一种干净的方式来表示一棵空树——你的方法要求总是至少有一个节点,因为这是你对树的引用。因此,要表示一棵空树,您需要创建一个值为 .这导致了很多问题,导致代码需要始终检查当前节点是否可能是这样的“僵尸”节点。您的代码并不总是这样做,因此仍然存在错误(例如在 中)。NoneNoneexists

但是,我建议创建第二个类,,其属性为(当树为空时)或对根节点的引用(实例)。然后将您需要的所有方法添加到类中 -- 如果相关,它们可以将此类调用转发给。但重要的优势是,现在你可以真正表示一棵空树,并且不需要代码来检测和管理“僵尸”节点。它还有助于为您的方法编写优雅的代码,这些代码可以非常简洁,因为它们可以依赖于类中具有相同名称的方法。BstrootNoneBstNodeBstNodeBstBstNode

这是我的一个答案,它提出了这种设计,并带有代码。

评论

0赞 Muhammad Olamide 11/8/2023
非常感谢,我将重构我的代码以使用单独的节点。如果我没有做对,将检查你的例子。