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