[算法-Python实现]查找二叉树

  1. 什么是查找二叉树
    查找二叉树是满足这样条件的不完全二叉树:对于任意的结点x,其左子树的所有关键字最大都不超过x的关键字,其右子树的所有关键字最小不小于x的关键字。
    其与最小堆的区别是,最小堆是完全二叉树的结构,且最小堆不要求左子树的关键字小于右子树。
    查找二叉树支持多种动态操作,比如按关键字搜索结点、 查找最小或最大关键字的结点、查找某个节点的前趋或后继节点、 插入和删除等操作,而且这些操作在最坏情况(树被构造成线性链表)时间复杂度是 Θ(n) , 最好情况(为一棵含n个节点的完全二叉树)为 Θ(lgn) , 平均情况下为 Θ(lgn) ,故其有很好地动态操作效率。
  2. 二叉查找树
    二叉查找树的结点有五个域:父结点p, 左子女left, 右子女right, 关键字key和卫星数据value,使用类Node表示:

        class Node:
            def __init__(self, key, value):
                self.key = key
                self.value = value
                self.p = None
                self.left = None
                self.right = None
    

    定义类BST来实现二叉查找树的数据结构:

    class BST:
    def init(self):
    self.root = None
    其中self.root指向查找树的根结点。通过在该类中定义INSERT、SEARCH等方法实现插入新的节点和按关键字搜索结点等操作。下面一步一步来实现。

  3. 顺序输出关键字(中序遍历)
    根据二叉查找树的性质,可以通过中序遍历(关键字的输出介于左子树和右子树关键字之间)的方法按从大到小的顺序输出树中的所有关键字:

    def InOrderTreeWalk(self, node):
            if node != None:
                self.InOrderTreeWalk(node.left)
                print node.key
                self.InOrderTreeWalk(node.right)
    

    其时间复杂度为 Θ(n)

  4. 查询二叉查找树
    除了支持最常用的按关键字查找SEARCH操作之外,二叉查找树还支持查找最大关键字MAXIMUM、最小关键字MINIMUM、查找后继节点SUCCESSOR、前趋结点查找PREDECESSOR等操作。对于高为h的结点,这些操作都可以在 O(h) 时间内完成。

    • SEARCH操作
      从根结点出发,比较结点关键字与查询关键字的大小,如果查询关键字更小的话则在比较结点的左子树上进行同样地查找操作,若果关键字查询更大,则在比较结点的右子树上继续进行查询操作,当然,如果相等,则当前比较结点就是所要查询的结点,将其返回。代码如下:
      递归版本:

      def TreeSearch(self, node, k):
              if node == None or k == node.key:
                  return node
              elif k < node.key:
                  return self.TreeSearch(node.left, k)
              else:
                  return self.TreeSearch(node.right, k)
      

      非递归版本:

      def IterativeTreeSearch(self, node, k):
          while k != node.key and node != None:
              if k < node.key: node = node.left
              else: node = node.right
          return node                     
      
    • 查询最大和最小关键字结点
      根据二叉查找树的性质,为了查找最小关键字结点,可以从某一结点出发,沿着各结点left指针查找下去,直到遇到None为止,这时就得到了最小关键字结点,同样地道理,沿着right指针查找下去就能找到最大关键字结点,代码如下:
      非递归版本:

      def TreeMinimum(self, node):
              while node.left != None:
                  node = node.left
              return node
      
      def TreeMaximum(self, node):
          while node.right != None:
              node = node.right
          return node
      

      递归版本:

      def RecursionTreeMinimum(self, node):
              if node.left != None:
                  return self.RecursionTreeMinimum(node.left)
              return node
      
      def RecursionTreeMaximum(self, node):
          if node.right != None:
              return self.RecursionTreeMaximum(node.right)
          return node       
      
    • 前趋和后继
      前趋结点:小于当前结点的所有结点中最大的那个;
      后继结点:大于当前结点的所有结点中最小的那个。
      很明显,前趋结点位于当前结点的左子树中沿着right指针指向的最后一个结点,后继结点位于当前结点右子树中沿着left指针指向的最后一个结点,代码实现如下:

      def TreeSuccessor(self, node):
              if node.right != None:
                  node = self.TreeMinimum(node.right)
              return node
      def TreePredecessor(self, node):
              if node.left != None:
                   node = self.TreeMaximum(node.left)
              return node              
      
  5. 插入和删除
    由于插入和删除操作会使二叉查找树表示的集合动态变化,所以要在操作过程中要修改数据结构。

    • 插入操作
      插入操作从根结点开始,比较关键字大小而沿着树下降,x跟踪这条路径,y指向x的父节点,当x指向None时,这个位置即为要插入结点的最终位置,y指向其父节点。

      def TreeInsert(self, node):
              '''
              y指针始终指向x的父节点
              '''
              y = None
              x = self.root
              while x != None:
                  y = x
                  if node.key < x.key:
                      x = x.left
                  else:
                      x = x.right
              node.p = y
              if y == None:
                  self.root = node
              elif node.key < y.key:
                  y.left = node
              else:
                  y.right = node
      
    • 删除操作
      删除操作比插入操作要复杂一些,需要考虑三种情况:所删结点没有子女、有一个子女、有两个子女。当没有子女时,直接将父节点指向其的指针置为None就可以了;当有一个子女时,还需要将被删除结点的子树与被删除结点的父节点互相指向;当有两个子女时,应该将该节点的后继结点删除,并把后继结点的key和value值赋给该节点。

      def TreeDelete(self, node):
              '''
              1st step: 将y指向将要被删除的结点(若node结点有一个子女或没有子女则y指向其本身, 若node有两个子女则指向node的后继结点)
              2nd step: 将x指向y的子女或None
              3rd step: 通过修改y.p和x将y删除
              4th step: 如果删除的是node的后继节点,则将y的值复制到node中
              :param node: 将要删除的结点
              :return:被删除的结点
              '''
              # 1st step
              if node.left == None or node.right == None:
                  y = node
              else:
                  y = self.TreeSuccessor(node)
              # 2nd step
              if y.left != None:
                  x = y.left
              else:
                  x = y.right
              # 3rd step
              if x != None:
                  x.p = y.p
              if y.p == None: #若是根节点
                  self.root = x
              elif y == y.p.left:
                  y.p.left = x
              else:
                  y.p.right = x
              # 4th step
              if y != node:
                  node.key = y.key
                  node.value = y.value
              return y                        
      

你可能感兴趣的:(算法,结构)