19_資料結構與演算法_查詢樹_Python實現
阿新 • • 發佈:2018-11-10
#Created By: Chen Da #構造一個樹節點的類 #以此作為BinarySearchTree的引用 class TreeNode(object): def __init__(self,key,value,left=None,right=None,parent=None): self.key = key self.value = value self.leftChild = left self.rightChild = left self.parent = parent def hasLeftChild(self): return self.leftChild def hasRightChild(self): return self.rightChild def isLeftChild(self): return self.parent and self.parent.leftChild == self def isRightChild(self): return self.parent and self.parent.rightChild == self def isRoot(self): return not self.parent def isLeaf(self): return not (self.leftChild or self.rightChild) def hasAnyChildren(self): return self.leftChild or self.rightChild def hasBothChildren(self): return self.leftChild and self.rightChild def replaceNodeData(self,key,value,leftChild,rightChild): self.key = key self.value = value self.leftChild = leftChild self.rightChild = rightChild if self.isLeftChild(): self.parent.leftChild = self if self.isRightChild(): self.parent.rightChild = self #定義二叉搜尋樹類(很像Python的字典) class BinarySearchTree(object): def __init__(self): self.root = None self.size = 0 def __len__(self): return self.size def length(self): return self.size def __iter__(self): return self.root.__iter__() def put(self,key,value): """ 此方法用來檢查樹是否具有根。 如果沒有根,那麼put將建立一個新的TreeNode並將其做為樹的根。 如果根節點已經就位,則put呼叫私有遞迴輔助函式_put根據以下演算法搜尋樹: 1、從樹的根開始,搜尋二叉樹,將新鍵與當前節點中的鍵進行比較。如果新鍵小於當前節點,則搜尋左子樹。如果新鍵大於當前節點,則搜尋右子樹。 2、當沒有左(或右)孩子要搜尋時,我們在樹種找到應該建立新節點的位置。 3、要向樹種新增節點,請建立一個新的TreeNode物件,並將物件插入到上一步發現的節點。 """ if self.root: self._put(key,value,self.root) else: self.root = TreeNode(key,value) self.size += 1 def _put(self,key,value,currentNode): if key < currentNode.key: if currentNode.hasLeftChild(): self._put(key,value,currentNode.leftChild) else: currentNode.leftChild = TreeNode(key,value,parent=currentNode) else: if currentNode.hasRightChild(): self._put(key,value,currentNode.rightChild) else: currentNode.rightChild = TreeNode(key,value,parent=currentNode) def __setitem__(self, key, value): self.put(key,value) def get(self,key): ''' 類似Python字典根據鍵取值 :param key: :return: ''' if self.root: result = self._get(key,self.root) if result: return result.value else: return None else: return None def _get(self,key,currentNode): if not currentNode: return None elif currentNode.key == key: return key elif key < currentNode.key: return self._get(key,currentNode.leftChild) else: return self._get(key,currentNode.rightChild) def __getitem__(self, item): return self.get(item) def __contains__(self, item): #實現in操作 if self._get(item,self.root): return True else: return False def delete(self,key): if self.size > 1: nodeToRemove = self._get(key,self.root) if nodeToRemove: self.remove(nodeToRemove) self.size -= 1 else: raise KeyError("Error,key not in tree!") elif self.size == 1 and self.root.key == key: self.root = None self.size -= 1 else: raise KeyError("Error,not in the tree!") def __delitem__(self, key): self.delete(key) def spliceOut(self): if self.isLeaf(): if self.isLeftChild(): self.parent.leftChild = None else: self.parent.rightChild = None elif self.hasAnyChildren(): if self.hasLeftChild(): if self.isLeftChild(): self.parent.leftChild = self.leftChild else: self.parent.rightChild = self.leftChild self.leftChild.parent = self.parent else: if self.isLeftChild(): self.parent.leftChild = self.rightChild else: self.parent.rightChild = self.rightChild self.rightChild.parent = self.parent def findSuccessor(self): ''' 尋找後繼。利用二叉搜尋樹的相同屬性,採用中序遍歷從最小到最大列印樹中的節點。 需要考慮三種情況: 1、如果節點有右子節點,則後繼節點是右子樹中的最小的鍵 2、如果節點沒有右子節點並且是父節點的左子節點,則父節點是後繼節點 3、如果節點是其父節點的右子節點,並且其本身沒有右子節點,則此節點的後繼節點是其父節點的後繼節點,不包括此節點。 :return: ''' succ = None if self.hasRightChild(): succ = self.rightChild.findMin() else: if self.parent: if self.isLeftChild(): succ = self.parent else: self.parent.rightChild = None succ = self.parent.findSuccessor() self.parent.rightChild = self return succ def findMin(self): #查詢子樹中最小的鍵 current = self while current.hasLeftChild(): current = current.leftChild return current def remove(self,currentNode): if currentNode.isLeaf(): #要刪除的節點沒有子節點 if currentNode == currentNode.parent.leftChild: currentNode.parent.leftChild = None else: currentNode.parent.rightChild = None elif currentNode.hasBothChild(): #要刪除的節點有兩個子節點 succ = currentNode.findSuccessor() succ.spliceOut() currentNode.key = succ.key currentNode.value = succ.value else: #要刪除的節點有一個子節點 if currentNode.hasLeftChild(): if currentNode.isLeftChild(): currentNode.leftChild.parent = currentNode.parent currentNode.parent.leftChild = currentNode.leftChild elif currentNode.isRightChild(): currentNode.leftChild.parent = currentNode.parent currentNode.parent.rightChild = currentNode.leftChild else: currentNode.replaeNodeData(currentNode.leftChild.key, currentNode.leftChild.value, currentNode.leftChild.leftChild, currentNode.leftChild.rightChild) else: if currentNode.isLeftChild(): currentNode.rightChild.parent = currentNode.parent currentNode.parent.leftChild = currentNode.rightChild elif currentNode.isRightChild(): currentNode.rightChild.parent = currentNode.parent currentNode.parent.rightChild = currentNode.rightChild else: currentNode.replaceNodeData(currentNode.rightChild.key, currentNode.rightChild.value, currentNode.rightChild.leftChild, currentNode.rightChild.rightChild)