簡體   English   中英

無法在python中的二進制搜索樹中實現刪除

[英]Trouble implementing deletion in Binary Search Tree in python

我已經實現了二進制搜索樹的刪除功能。 這個想法是聲明一個私有函數,該函數需要一個額外的參數來將self.root抽象為root 在私有刪除功能中,它將執行條件檢查並確保root等於需要刪除的數據。 經過條件檢查后,我編寫了3種不同的刪除案例。 編譯代碼時沒有錯誤消息,也不會刪除任何插入的節點。

class Node(object):
    def __init__(self, data, left=None, right=None, parent=None):
        self.left = left  
        self.data = data
        self.right = right   


class Tree(object):
    def __init__(self):
        self.root = Node(None)

    def delete(self,newData):
        if self.root.data == None:
            print 'The tree is empty'
        else:
            self.__delete(self.root, newData)

    def __delete(self, root, newData):
        # if newData smaller than root.data, change root to root.left, recursive call
        if newData < root.data:
            if root.left:
                self.__delete(root.left, newData)
            else:
                return False
        # if newData larger than root.data, change root to root.right, recursive call
        elif newData > root.data:
            if root.right:
                self.__delete(root.right, newData)
            else:
                return False
        elif newData == root.data:
            #case 1, root has no child
            if root.left is None and root.right is None:
                root = None
            #case 2, root has one child (left)
            elif root.left is not None and root.right is None:
                root.data = root.left.data
                root.left = None
            #case 3, root has one child (right)
            elif root.right is not None and root.left is None:
                root.data = root.right.data
                root.right = None
            #case 4, root has both children,
            # find the smallest node in the right subtree, and swipe value
            # delete smallest node in the right subtree
            else:
                root.data = self.__minValueToRightSubtree(root.right).data
                self.__deleteMinValueToRightSubtree(root.right)
        else:
            print "Can't find this number"

    def __minValueToRightSubtree(self, root):
        if root.left is None:
            return root
        else:
            return self.__minValueToRightSubtree(root.left)

    def __deleteMinValueToRightSubtree(self, root):
        if root.left is None:
            root = None
            return root
        else:
            self.__minValueToRightSubtree(root.left)

不幸的是,您的遞歸函數的基本情況都無法正常工作。 錯誤有兩種(每種重復兩次,有一些變化):

第一個問題很簡單。 在情況2和3中,您要從單個子節點復制數據,然后刪除對該節點的引用。 但是,如果子節點有其自己的子節點,則此操作將不正確。 如果可以保證您的樹是平衡的,也許您可​​以假設它沒有子對象,但是對於一般的BST,您不能假設它是孩子。 更好的版本是:

        #case 2, root has one child (left)
        elif root.left is not None and root.right is None:
            root.data = root.left.data
            root.right = root.left.right
            root.left = root.left.left
        #case 3, root has one child (right)
        elif root.right is not None and root.left is None:
            root.data = root.right.data
            root.left = root.left.left
            root.right = root.left.right

另一個問題更加微妙。 關鍵是,你不能刪除root你想在1的情況下做的方式(並在箱體4 __deleteMinValueToRightSubtree helper方法)。 您正在為root分配None ,如果Python以與C ++和Java相同的方式傳遞參數(通過引用),則這可能會起作用。 但是Python的論點不同於那些語言。 Python參數是通過“賦值”傳遞的,這意味着函數中具有的參數是局部變量,綁定到與調用方傳入的對象相同的對象。當您執行root = None ,您僅在修改局部變量,並且不是樹形結構。

您可以通過多種方法來解決此問題。 哪種方法最好取決於您實施的其他細節。

如果您的Node對象具有parent引用,則可以使用那些引用來取消節點與其父級的鏈接(盡管對於沒有父級的根節點,您將需要特殊的情況)。 我看到了Node構造函數的parent參數,但是您似乎並沒有使用它。 如果將其連接起來,則用於刪除節點的代碼將相對容易。

        #case 1, root has no child
        if root.left is None and root.right is None
            if root.parent is None: # root is the root node of the whole tree
                self.root = None
            elif root.parent.left is root:
                root.parent.left = None
            else: # elif root.parent.right is root:
                root.parent.right = None
        #...
        #case 4, root has both children,
        # find the smallest node in the right subtree, and swipe value
        # delete smallest node in the right subtree
        else:
            min_right_node = self.__minValueToRightSubtree(root.right)
            root.data = min_right_node.data       # no need to recurse twice
            if min_right_node is self.right:      # we can use the same node reference for
                self.right = None                 # both steps (swiping value and deleting)
            else:
                min_right_node.parent.left = min_right_node.right

如果沒有父鏈接,則可以更改遞歸的邏輯,以便return修改后的樹,調用者將其分配給要在其上進行遞歸的節點。 這將需要您更改錯誤處理,因為返回值除了用於表示成功或失敗以外,還用於其他方面。 如果沒有找到目標,我建議提出一個例外。

def delete(self,newData):
    if self.root.data == None: # should this be testing `self.root is None`?
        print 'The tree is empty'
    else:
        self.root = self.__delete(self.root, newData) # use return value

def __delete(self, root, newData):
    # if newData smaller than root.data, change root to root.left, recursive call
    if newData < root.data:
        if root.left:
            root.left = self.__delete(root.left, newData)
        else:
            raise ValueError("Can't find this number")
    # if newData larger than root.data, change root to root.right, recursive call
    elif newData > root.data:
        if root.right:
            root.right = self.__delete(root.right, newData)
        else:
            raise ValueError("Can't find this number")
    elif newData == root.data:
        #case 1, root has no child
        if root.left is None and root.right is None:
            return None
        #case 2, root has one child (left)
        elif root.left is not None and root.right is None:
            return root.left
        #case 3, root has one child (right)
        elif root.right is not None and root.left is None:
            return root.right
        #case 4, root has both children,
        # find the smallest node in the right subtree, and swipe value
        # delete smallest node in the right subtree
        else:
            root.right, root.data = __delete_min(root.right)
            return root
    else:
        print "Can't find this number"

def __delete_min(self, root): # returns a (node, minimum value) 2-tuple
    if root.left is None:
        return root.right, root.data
    else:
        root.left, minval = self.__delete_min(root.left)
        return root, minval

關於命名的最后一句話:對私有函數使用雙引號下划線名稱是一個壞主意。 該語法調用了Python的“名稱修改”系統,該系統將名稱轉換為引用定義了引用它們的代碼的類。 當您編寫mixin或代理類時,它非常有用,並且您無法提前知道哪些屬性可能會發生沖突。 但是對於普通代碼,這只會使事情煩人。 如果要將方法標記為私有,只需使用一個下划線。 這在語言級別上沒有任何作用,但這是一個約定。 其他程序員(和文檔工具)將知道以這種方式命名的函數不屬於公共API。 (另一種可能更弱的慣例是,對於大多數變量和方法,使用lowercase_names_with_underscores而不是camelCaseNames 。這更多的是樣式問題,實際上並不是對使用代碼有害的東西,例如名稱修改。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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