2017-06-28 4 views
2

私はバイナリ検索ツリーを持っており、このツリーの高さを取得しようとしています。 insert_element(self、value)を実行するたびにインクリメントするself.height属性があり、remove_element(self、value)が発生するとデクリメントされます。しかし、私は、メソッドの1つが発生するたびに増減することに気づき、ノードが高さを変更しない同じ高さにあった場合は考慮しません。バイナリ検索ツリーで一定の時間内に高さを取得する方法は?

class Binary_Search_Tree: 

    class __BST_Node: 

     def __init__(self, value): 
      self.value = value 
      self.lchild = None 
      self.rchild = None 

    def __init__(self): 
     self.__root = None 
     self.height = 0 

    def insert_element(self, value): 
     self._insert(self.__root, value) 

    def _insert(self, root, value): 
     node = Binary_Search_Tree.__BST_Node(value) 
     if root == value: 
      raise ValueError("Value already exists") 
     if self.__root is None: 
      self.__root = Binary_Search_Tree.__BST_Node(value) 
      self.height += 1 
      return self.__root 
     else: 
      if root.value > node.value: 
       if root.lchild is None: 
        root.lchild = node 
        self.height += 1 
        return root.lchild 
       else: 
        self._insert(root.lchild, value) 
      elif root.value < node.value: 
       if root.rchild is None: 
        root.rchild = node 
        self.height += 1 
        return root.rchild 
       else: 
        self._insert(root.rchild, value) 
     return root 

    def remove_element(self, value): 
     self.__root = self._remove(self.__root, value) 
     self.height -= 1 
     return self.__root 

    def _remove(self, root, value): 
     if self.__root is None: 
      raise ValueError("Tree is empty") 
     if root.value != value: 
      raise ValueError("No such value") 
     if root.value == value: 
      if root.lchild is None and root.rchild is None: 
       root = None 
       self.height -= 1 
       return root 
      elif root.lchild is None or root.rchild is None: 
       if root.lchild is None: 
        self.height -= 1 
        return root.rchild 
       if root.rchild is None: 
        self.height -= 1 
        return root.lchild 
      elif root.lchild and root.rchild: 
       parent = root 
       successor = root.rchild 
       while successor.lchild: 
        parent = successor 
        successor = successor.lchild 
       root.value = successor.value 
       if parent.lchild == successor: 
        parent.lchild = successor.rchild 
        self.height -= 1 
        return parent.lchild 
       else: 
        parent.rchild = successor.rchild 
        self.height -= 1 
        return parent.rchild 
     else: 
      if root.value > value: 
       if root.lchild: 
       root.lchild = self._remove(root.lchild, value) 
     elif root.value < value: 
      if root.rchild: 
       root.rchild = self._remove(root.rchild, value) 
     return root 

    def in_order(self): 
     if self.__root is not None: 
      self._in(self.__root) 

    def _in(self, root): 
     if root is None: 
      return 
     if root is not None: 
      self._in(root.lchild) 
      print(str(root.value)) 
      self._in(root.rchild) 

    def get_height(self): 
     print(str(self.height)) 

    def __str__(self): 
     return self.in_order() 


if __name__ == '__main__': 
    pass 
+1

回答があります(https://stackoverflow.com/a/15193346/3350448)。 – PidgeyUsedGust

+0

一方、 'if root == value:'を挿入すると、既存の値を間違って処理します。また、ノード要素を実際に作成してから、値を比較してどこにあるべきかを評価するべきではありません。 –

答えて

0

ここでは、ジョブを実行する必要があるツリーのバージョンです。 私は元のコードにできるだけ似通っているようにしようとしましたが、サンプルの作り方を理解しようとしながら少し変更しなければなりませんでした。

キーパーツを認識して必要に応じて再利用するには、まだ十分に近いことを望みます。

class Binary_Search_Tree: 

    class __BST_Node: 

     def __init__(self, value, height): 
      self.value = value 
      self.height = height 
      self.lchild = None 
      self.rchild = None 

    def __init__(self): 
     self.__root = None 
     self.__height = 0 

    def _insert(self, knot, value): 
     if knot is None: 
      self.__root = Binary_Search_Tree.__BST_Node(value, 1) 
      result = self.__root 
     elif knot.value == value: 
      # replace error with WARNING, to handle exception 
      # raise ValueError("Value already exists") 
      print 'WARNING: value', value, 'already exists; skipped' 
      return knot 
     elif knot.value > value: 
      if knot.lchild is None: 
       knot.lchild = Binary_Search_Tree.__BST_Node(value, knot.height + 1) 
       result = knot.lchild 
      else: 
       result = self._insert(knot.lchild, value) 
     else: # knot.value < value 
      if knot.rchild is None: 
       knot.rchild = Binary_Search_Tree.__BST_Node(value, knot.height + 1) 
       result = knot.rchild 
      else: 
       result = self._insert(knot.rchild, value) 
     self.__height = max(self.__height, result.height) 
     return result 

    def _remove(self, knot, value): 
     """delete the knot with the given value 
     based on: https://stackoverflow.com/a/33449471/8200213 
     """ 
     if knot.value == value: # found the node we need to delete 
      successor = None 
      if knot.rchild and knot.lchild: 
       # get the successor node and its parent 
       successor, parent = knot.rchild, knot 
       while successor.lchild: 
        successor, parent = successor.lchild, successor 
       # splice out successor (the parent must do this) 
       if parent.lchild == successor: 
        parent.lchild = successor.rchild 
       else: 
        parent.rchild = successor.rchild 
       # reset the left and right children of the successor 
       successor.lchild = knot.lchild 
       successor.rchild = knot.rchild 
       self._update_heights(successor, knot.height) 
       return successor 
      # else (not knot.rchild or/and not knot.lchild) 
      if knot.lchild:  # promote the left subtree 
       self._update_heights(knot.lchild, knot.height) 
       return knot.lchild 
      elif knot.rchild: # promote the right subtree 
       self._update_heights(knot.rchild, knot.height) 
       return knot.rchild 
      # else: no children 
      self._update_heights(knot, knot.height) 
      return 
     # else: keep traversing 
     if knot.value > value and knot.lchild is not None: 
      # value should be in the left subtree 
      knot.lchild = self._remove(knot.lchild, value) 
     elif knot.value < value and knot.rchild is not None: 
      # value should be in the right subtree 
      knot.rchild = self._remove(knot.rchild, value) 
     # else: the value is not in the tree 
     return knot 

    def _update_heights(self, knot, height, maxheight=0): 
     # print 'update from knot value', knot.value, 'with height', knot.height, 'to height', height 
     maxheight = max(maxheight, knot.height) 
     knot.height = height 
     if knot.lchild is not None: 
      self._update_heights(knot.lchild, knot.height + 1, maxheight) 
     if knot.rchild is not None: 
      self._update_heights(knot.rchild, knot.height + 1, maxheight) 
     if maxheight == self.__height: 
      # the max height of the whole tree might have changed; re-compute 
      self.__height = -1 

    def _recompute_height(self, knot): 
     if not knot: 
      return 
     self.__height = max(self.__height, knot.height) 
     if knot.lchild is not None: 
      self._recompute_height(knot.lchild) 
     if knot.rchild is not None: 
      self._recompute_height(knot.rchild) 

    def _get_ordered(self, knot, pre=False, nodelist=None): 
     nodelist = nodelist or [] 
     if knot is None: 
      return nodelist 
     if not pre: 
      nodelist = self._get_ordered(knot.lchild, pre, nodelist) 
     nodelist.append(knot.value) 
     if pre: 
      nodelist = self._get_ordered(knot.lchild, pre, nodelist) 
     nodelist = self._get_ordered(knot.rchild, pre, nodelist) 
     return nodelist 

    def insert_element(self, value): 
     self._insert(self.__root, value) 
     # print self.__height 

    def remove_element(self, value): 
     self.__root = self._remove(self.__root, value) 
     # print self.get_height() 

    def get_pre_order(self): 
     return self._get_ordered(self.__root, True) 

    def get_in_order(self): 
     return self._get_ordered(self.__root) 

    def get_height(self): 
     if self.__height == -1: 
      # self.__height was marked "dirty" 
      # re-computing tree height, as it might have changed 
      self._recompute_height(self.__root) 
     return self.__height 

def __str__(self): 
     return ', '.join(['%d' % val for val in self.get_in_order()]) 

PidgeyUsedGustによって投稿linkで示唆したように、あなたはおそらくノードに高さを格納したいので、あなたは簡単に任意の新たに挿入されたノードの高さと木の全体の高さの両方を計算することができます。

ノードを削除した後の高さを最新の状態に保つことは、それほど簡単です。私が出てきたのは、コードのシンプルさとパフォーマンスの間に適切な妥協を図ろうとしている半初歩的な実装です(時間は一般的に許容できるが一定ではないことを意味します。取る)。ここで

はアイデアです:

  • ノードを削除するたびに、あなたは木の残りの部分をそのまま残し、そのノードの高さとそのすべての子孫を更新したいです。これは、次回
  • self.__height = -1_update_heights方法は、私たちは木属性を
  • そうすることが、我々は(更新前に)、の高さをノードを打つたびに全体の高さに等しいマークしないものを「ダーティ」であります我々はその高さが変わっている場合があります知っていると_recompute_height方法を呼び出すことにより、再コンピューティングを必要とするでしょうself.__height == -1場合、私たちは、木の上にget_heightと呼ぶことにします

_recompute_heightのみget_heightによってトリガーされる理由はなく、によって_update_heightsは、要素が存在するたびに属性を再計算することを避けることですそれが必要でない場合は削除されます(つまり、 1000個の要素を削除し、ツリーの新しい高さを1回だけチェックする必要があります)。ここで

コードをテストするために数行(drawtreeモジュールが使用されている:あなたはまだなかった場合は、それをインストールする必要があります):

if __name__ == '__main__': 

    bst = Binary_Search_Tree() 
    values = [7, 2, 2, 5, 1, 8, 3, 6, 9, 8, 4, 11, 10, 12] 
    print 'insert values', values 
    for val in values: 
     bst.insert_element(val) 
    print 'tree successfully built\nheight: %d' % bst.get_height() 
    print 'ordered: %s' % (bst,) 
    print 'pre-ordered:', bst.get_pre_order(), '\n' 

    import drawtree 
    drawtree.draw_bst(bst.get_pre_order()) 

    values = [4, 2, 7, 4] 
    for val in values: 
     print '\n\nremove value %d\n' % val 
     bst.remove_element(val) 
     drawtree.draw_bst(bst.get_pre_order()) 

    print '\n\nnew height: %d' % bst.get_height() 

そして、ここでは、あなたが得る必要があり、結果です。

insert values [7, 2, 2, 5, 1, 8, 3, 6, 9, 8, 4, 11, 10, 12] 
WARNING: value 2 already exists; skipped 
WARNING: value 8 already exists; skipped 
tree successfully built 
height: 5 
ordered: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 
pre-ordered: [7, 2, 1, 5, 3, 4, 6, 8, 9, 11, 10, 12] 

    7 
    /\ 
/ \ 
    2  8 
/\  \ 
1 5  9 
/\  \ 
    3 6 11 
    \  /\ 
    4 / \ 
     10 12 


remove value 6 

    7 
/\ 
    2 8 
/\ \ 
1 5 9 
/ \ 
    3  11 
    \ /\ 
    4 / \ 
     10 12 


remove value 2 

    7 
/\ 
    3 8 
/\ \ 
1 5 9 
/ \ 
    4  11 
     /\ 
     / \ 
     10 12 


remove value 7 

    8 
    /\ 
/ \ 
    3  9 
/\  \ 
1 5 11 
/ /\ 
    4 / \ 
     10 12 


new height: 4 
関連する問題