zoukankan      html  css  js  c++  java
  • 二叉查找树BST

    1.什么是二叉查找树

    在ADT MAP的实现方案中,可以采用不同的数据结构与搜索算法来保存和查找key。

    • 有序表数据结构+二分查找法
    • 散列表数据结构+散列及冲突解决算法

    这里我们将尝试用二叉查找树保存key,实现key值的快速搜索。

    2.二叉查找树的性质

    比父节点小的key都出现在左子树中,比父节点大的key都出现在右子树中,这个性质可以看出二叉查找树时递归性质的。

                  70
            31          93
        14     null   73   94
           23
    
    			31 
            23      93
         14      73     94
               70
    

    我们可以看到同一组数据(70,31,93,94,14,23,73)根据插入的顺序不同,生成的bst也是不一样的。

    3.二叉树的实现及算法分析

    class BinarySearchTree:
        def __init__(self):
            self.root = None
            self.size = 0
    	
        def length(self):
            return self.size
        
        def __len__(self):
            return self.size
        
        def __iter__(self):
            return self.root.__iter__()
        
        def put(self,key,val):
            if self.root:
                self._put(key,val,self.root)
            else:
                self.root = TreeNode(key,val)
            self.size +=1
        
        # 思路:key小于当前节点的key,并且当前节点有左节点,则_put递归调用currentNode的节点为当前currentNode的左节点,如果当前节点没有左节点,则插入的key,val成为当前节点的左节点。同理。
        def _put(key,val,currentNode):
            if key < currentNode:
                if currentNode.hasLeftChild():
                	self._put(key,val,currentNode.leftChild)
                else:
                    currentNode.leftChild = TreeNode(key,val,parent=currentNode)
            else:
                if currentNode.hasRightChild:
                	self._put(key,val,currentNode.rightChild)
             	else:
                    currentNode.rightChild = TreeNode(key,val,parent=currentNode)
    	# tree_obj[k] = v时触发
        def __setitem__(self,k,v):
            self.put(key,val)
    	
        def get(self,key):
            # 从根节点开始找
            if self.root:
                res = self._get(key,self.root)
    			if res:
                	return res.payload
    			else:
                    return None
            else:
                return None
        
        def _get(self,key,currentNode):
            if not currentNode:
                return None
            if currentNode.key == key:
                return currentNode
            elif currentNode.key > key:  
                return self._get(key,currentNode.leftChild)
            else:
                return self._get(key,currentNode.rightChild)
        
        def __getitem__(self,key):
            return self.get(key)
        
        def __contains__(self,key):
            if self._get(key,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 = 0
            else:
                raise KeyError('Error,key not in tree')
    	
        def __delitem__(self,key):
            self.delete(key)
        
    '''
    从BST中remove一个节点,还要求仍然保持BST的性质。
    分情况讨论:
    	1.该节点是叶子节点
    	2.该节点只有左节点或右节点
    	3.该节点同时有左节点和右节点
    '''
    	def remove(self,currentNode):
            if currentNode.isLeaf():
                if currentNode == currentNode.parent.leftChild:
                    currentNode.parent.leftChild = None
                else:
                    currentNode.parent.rightChild = None
            # 有两个节点
            '''
            此时无法就简单地进行替换被删节点,但可以找到另一个合适地节点来替换被删节点,整个合适节点就是被删节		点的下一个key值节点。即被删除节点右子树中做小的那个,称为‘后继’。 这个‘后继’ 只能是叶子节点或者是只有一个右节点。 
            '''
            elif currentNode.hasBothChild():
            	succ = currentNode.findSuccessor()
                succ.spliceOut()
                currentNode.key = succ.key
                currentNode.payload = succ.payload
            
                
            # 只有一个节点
            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.rightChild
                    else: # 当前节点有一个左节点,并且它本身既不是左节点也不是右节点(根节点)
                        # self.root = currentNode.leftChild
                        currentNode.replaceNodeData(
                            currentNode.leftChild.key,
                            currentNode.leftChild.payload,
                            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:# 当前节点有一个右节点,并且它本身既不是左节点也不是右节点(根节点)
                        # self.root = currentNode.rightChild
                      	currentNode.replaceNodeData(
                            currentNode.rightChild.key,
                            currentNode.rightChild.payload,
                            currentNode.rightChild.leftChild,
                            currentNode.rightChild.rightChild)
             	
        
        
    class TreeNode:
        def __init__(self,key,val,left=None,rigth=None,parent=None):
            self.key = key
            self.payload = val
            self.leftChild = left
            self.rightChild = right
            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.rightChild or self.leftChild)
        
        def hasAnyChildren(self):
            return self.rightChild or self.leftChild
        
        def hasBothChild(self):
            return self.leftChild and self.rightChild
        
       	def replaceNodeData(self,key,val,lc,rc):
            self.key = key
            self.payload = value
            self.leftChild = lc
            self.rightChild = rc
            if self.hasLeftChild():
                self.leftChild.parent = self
            if self.hasRightChild():
                self.rightChild.parent = self
                
    	# 中序遍历     
        def __iter__(self):
            if self:
                if self.hasLeftChild():
                    # 递归调用
                    for elem in self.leftChild:
                        yield elem
    			yield self.key
                if self.hasRihtChild():
                    for elem in self.rightChild:
                        yield elem
    
    	def findSuccessor(self):
            succ = None
            if self.hasRightChild():
                succ = self.rightChild.findMin()
            else: # 其实可以不考虑这个情况,findSuccessor被调用的前提时在当前节点同时有左右节点的情况下。
                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 spliceOut(self):
            if self.isLeaf():
                if self.isLeftChild():
                    self.parent.leftChild = None
                else:
                    self.parent.rightChild = None
    		elif self.hasAnyChild():
                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
                    
    

    4.小结

    # 算法分析 (以put为例)
    # 其性能决定因素在于二叉搜索树的高度,而高度收到数据项key插入顺序的影响。
    # 如果key的列表是随机分布的,那么大于和小于根节点key的键值大致相等,BST的高度就是logn(n是节点个数),而且这样的树就是平衡树,此时put方法最差性能为logn。
    
    # 但是如果key列表分布的极端情况就完全不同。树向一边完全倾斜(类似于单列表的情况)有几个节点高度就为多少。
    
  • 相关阅读:
    定时器
    SysTick
    13.看门狗
    12.FSMC
    11.中断
    ##HTML行内块元素好迷啊!!
    ##HTML基础
    ##CS3动画效果
    ##管家婆项目(tools层)
    ##管家婆项目(app层)
  • 原文地址:https://www.cnblogs.com/Ghostant/p/12236260.html
Copyright © 2011-2022 走看看