1. 程式人生 > >19_資料結構與演算法_查詢樹_Python實現

19_資料結構與演算法_查詢樹_Python實現

#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)