二叉搜索树

224 阅读5分钟

1/定义

二叉搜索树(Binary Search Tree),又二叉排序树(Binary Sort Tree)。
从字面上理解,它首先是一棵二叉树,那么就满足二叉树的性质。每个节点的最大度是2。
之所以称之为二叉搜素树或者二叉排序树,是因为它具有一定的性质:
    <1>若它的左子树不空,则左子树上所有节点的`值均`小于它的根节点的值
    <2>若它的右子树不空,则右子树上所有节点的`值均`大于它的根节点的值; 
    <3>它的左、右子树也分别为二叉搜索树。
    <4>对于任何一个非叶子节点,如果以该节点为root节点,则也是一棵二叉搜索树。
    
二叉搜索树作为一种经典的数据结构,它既有链表的快速插入与快速删除的特点,又有数组快速查找的优势;
所以应用十分广泛,例如在文件系统和数据库系统一般会采用这种数据结构进行高效率的排序与检索操作。 

image.png

2/二叉搜索树的相关操作

2.1/插入操作

从根节点开始,
若插入的值比根节点的值小,则将其插入根节点的左子树;
若比根节点的值大,则将其插入根节点的右子树。
该操作可使用`递归`进行实现。
   class TreeNode():
       def __init__(self,data=None,left=None,right=None):
           self.data = data
           self.left = left
           self.right = right
           
         
   def insert(self, root, data): 
       # 二叉搜索树插入操作
       # root是一棵树的根节点(当然这棵树可以是None),data是待插入的节点
       
       # 从根节点开始
       # 如果根节点为空,则把data直接作为根节点,插入结束
       if root == None:   
           root = TreeNode(data) 
       
       # 如果根节点不是空,则判断data与左右节点的大小
       # 如果data比根节点的值小,则递归,此时root参数变成了原来根节点的左孩子,及root.left
       # 如果data比根节点的值大,则递归,此时root参数变成了原来根节点的右孩子,及root.right
       # 一直等到root.left或者root.right为空,这样可以直接调用TreeNode()给该节点赋值,结束。
       elif data < root.value:  
           root.left = self.insert(root.left, data)  # 一直等到root.
       
       elif data > root.value: 
           root.right = self.insert(root.right, data) 
           
       return root

2.2/查询操作

从根节点开始查找,待查找的值是否与根节点的值相同,若相同则返回True;
否则,判断待寻找的值是否比根节点的值小,若是则进入根节点左子树进行查找,
否则进入右子树进行查找。
该操作使用`递归`实现。
def query(self, root, data): 
    # 二叉搜索树查询操作
    # 查找某个节点是否在二叉树中
    # 前提是:这是一棵搜索二叉树(及满足左孩子比父节点小,右孩子比父节点大的性质)
    
    # 如果给的二叉搜索树就是空,则直接返回False
    # 意思就是已经找到末尾了,还是没有找到,则返False
    if root == None: 
        return False 
    
    # 如果根节点的value就是要查找的data,则直接返回True
    if root.value == data: 
        return True 
        
    # 待查找data小于根节点,则往左孩子方向去递归查找
    elif data < root.value: 
        return self.query(root.left, data)
    
    # 待查找data大于根节点,则往右孩子方向去递归查找
    elif data > root.value: 
        return self.query(root.right, data)

2.3/查找二叉搜索树中的最大(小)值

(1)查找最小值:从根节点开始,沿着左子树一直往下,直到找到最后一个左子树节点,
    按照定义可知,该节点一定是该二叉搜索树中的最小值节点。
  def find_min_value(self, root): 
      # 查找二叉搜索树中最小值点
      # root是传入的一棵二叉搜索树
      
      if root.left: 
          # 一直找左孩子root.left,直到root.left为假
          return self.find_min_value(root.left) 
      else: 
          return root
 (2)查找最大值:从根节点开始,沿着右子树一直往下,直到找到最后一个右子树节点,
      按照定义可知,该节点一定是该二叉搜索树中的最大值节点。
   def find_max_value(self, root): 
       # 查找二叉搜索树中最大值点
       if root.right: 
           # 一直找右孩子,知道root.right是假的
           return self.find_max_value(root.right) 
       else: 
           return root

2.4/删除节点操作

对二叉搜索树节点的删除操作分为以下三种情况:

  (1)待删除节点既无左子树也无右子树: 直接删除该节点即可 也就是说;待删除的节点是叶子节点,则可以实际删除 image.png

(2)待删除节点只有左子树或者只有右子树:
     将其左子树或右子树根节点代替待删除节点

image.png

(3)待删除节点既有左子树也有右子树:
     找到该节点右子树中最小节点,使用该节点代替待删除节点,
     然后在右子树中删除最小节点。

image.png

def delNode(self, root, data): 
    # 删除二叉搜索树中值为val的点
    # root是给定的一棵二叉搜索树,data是待删除的节点
    
    if root == None: 
        return 
        
    if data < root.value: 
        root.left = self.delNode(root.left, data)
        
    elif data > root.value: 
        root.right = self.delNode(root.right, data) 
       
    # 当data == root.value时,
    # 及找到要删除的节点的时候
    # 分为三种情况:有左右子树,即无左子树又无右子树,只有左子树或者只有右子树
    else: 
        if root.left and root.right: 
            # 既有左子树又有右子树,则需找到右子树中最小值节点 
            temp = self.findMin(root.right) 
            root.value = temp.value
            # 再把右子树中最小值节点删除 
            root.right = self.delNode(root.right, temp.value) 
            
        elif root.right == None and root.left == None: 
            # 左右子树都为空 
            root = None # 直接删除该节点即可
            
        elif root.right == None: 
            # 只有左子树 
            root = root.left 
        
        elif root.left == None: 
            # 只有右子树 
            root = root.right 
            
    return root
    

2.5/打印操作

实现二叉搜索树的中序遍历,并打印出来。
该方法打印出来的数列将是按照递增顺序排列。
    def printTree(self, root): 
        # 打印二叉搜索树(中序打印,有序数列) 
        if root == None: 
            return 
        self.printTree(root.left)     # 先打印左子树
        print(root.value, end = ' ')  # 然后打印中间节点
        self.printTree(root.right)    # 再打印右子树