Home / 別館 Home / Ruby / アルゴリズム / ツリー構造
| 掲示板 | Mail |

ツリー構造

2分木の操作を行う

基本的な考え方

 ツリーはリスト構造の延長線上として考えればいい。

 データの追加、削除、検索が効率よく行える。バイナリサーチであった「予めデータをソートしなければならない」「配列を使うので、データの頻繁な追加と削除を行うのに向かない」という欠点をクリアしている。


2分木のデータ追加、サーチ、削除 (List6-4)
# Node Class
class Node
  attr_reader :value, :left, :right
  def initialize( val )
    @value = val   # このノードが保持する値
    @left = nil    # 左側のノード
    @right = nil   # 右側のノード
  end
  
  # ノードの値を設定
  def setValue( val )
    @value = val
  end

  # 左側のノードの設定
  def setLeft( node )
    @left = node
  end
  
  # 右側のノードの設定
  def setRight( node )
    @right = node
  end
end


# Tree Class
class TreeNode
  attr_reader :treeRoot
  
  def initialize()
    @treeRoot = nil    # ツリーのルート
  end
  
  # ----- ノードの追加
  def insertNode( num, node = nil )
    # 1つも挿入されていない場合
    if node == nil
      @treeRoot = createNewNode( num )
      return
    end
    
    # num が現在の node の値よりも小さい場合は左側に追加する
    if node.value > num
      if node.left != nil
        insertNode(num, node.left)
      else
        node.setLeft( createNewNode( num ) )
      end
    # num が現在の node の値以上の場合は右側に追加する
    else
      if node.right != nil
        insertNode(num, node.right)
      else
        node.setRight( createNewNode( num ) )
      end
    end
  end
  
  # ----- ノードの検索
  def findValue( val, node = nil )
    # 自node よりも小さい値なら左側を検索
    if node.value > val
      if node.left == nil
        return nil
      end
      return findValue( node.left, val )
    end
    # 自node よりも大きい値なら右側を検索
    if node.value < val
      if node.right == nil
        return nil
      end
      return findValue( node.right, val )
    end
    return node
  end
  
  # ----- ノードの削除
  def deleteNode( val )
    node = @treeRoot
    parentNode = nil
    direction = 0
    
    # 削除すべき対象を発見する
    while (node != nil && node.value != val)
      if node.value > val
        parentNode = node
        node = node.left
        direction = -1
      else
        parentNode = node
        node = node.right
        direction = 1
      end
    end
    
    if node == nil
      return false
    end
    
    if (node.left == nil || node.right == nil)
      if node.left == nil
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.right )
        elsif direction == 1
          parentNode.setRight( node.right )
        elsif direciton == 0
          @treeRoot = node.right
        end
      else
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.left )
        elsif direction == 1
          parentNode.setRight( node.left )
        elsif direction == 0
          @treeRoot = node.left
        end
      end
    else
      # node の左側の最も大きい値を取得する
      leftBiggest = node.left
      parentNode = node
      direction = -1
      while leftBiggest.right != nil
        parentNode = leftBiggest
        leftBiggest = leftBiggest.right
        direction = -1
      end
      
      # node の入れ替え
      node.setValue( leftBiggest.value )
      
      if direction == -1
        parentNode.setLeft( leftBiggest.left )
      else
        parentNode.setRight( leftBiggest.left )
      end
    end
    return true
  end
  
  def printNode( depth, node = nil )
    if node == nil
      return
    end
    printNode( depth + 1, node.left )
    i = 0
    while i < depth
      printf "   "
      i += 1
    end
    printf "#{node.value}¥n"
    printNode( depth + 1, node.right )
  end
  
  def main
    action = nil
    while action != 0
      printNode( 0, @treeRoot )
      puts "実行する操作のタイプを入力して下さい"
      puts "1:追加  2:検索  3:削除  0:終了 >"
      action = gets.chomp.to_i
      
      case action
        when 1
          puts "1〜100までの範囲で追加する数値を入力して下さい"
          i = gets.chomp.to_i
          if (i < 1 || i > 100)
            continue
          end
          insertNode( i, @treeRoot )
        when 2
          puts "検索する数字を入力して下さい"
          i = gets.chomp.to_i
          if findValue( i, @treeRoot ) != nil
            puts "#{i}を発見しました"
          else
            puts "#{i}は見つかりませんでした"
          end
        when 3
          puts "削除する数字を入力して下さい"
          i = gets.chomp.to_i
          if deleteNode(i)
            puts "#{i}を削除しました"
          else
            puts "#{i}は見つかりませんでした"
          end
      end
    end
  end
  
  private
  # ノードを生成する
  def createNewNode( val )
    newNode = Node.new( val )
    newNode.setLeft( nil )
    newNode.setRight( nil )
    return newNode
  end
end


# Main 
tree = TreeNode.new
tree.main
2分木のデータ追加、サーチ、削除 (もうちょっと Ruby っぽく)
# Node Class
class Node
  attr_reader :value, :left, :right
  
  def initialize( val )
    @value = val    # このノードが保持する値
    @left = nil     # 左側のノード
    @right = nil    # 右側のノード
  end
  
  # ノードの値を設定
  def setValue( val )
    @value = val
  end

  # 左側のノードの設定
  def setLeft( node )
    @left = node
  end
  
  # 右側のノードの設定
  def setRight( node )
    @right = node
  end
end


# Tree Class
class TreeNode
  attr_reader :treeRoot
  
  def initialize()
    @treeRoot = nil    # ツリーのルート
  end
  
  # ----- ノードの追加
  def insertNode( val, node = @treeRoot )
    # 1つも挿入されていない場合
    if node == nil
      @treeRoot = createNewNode( num )
      return
    end
    
    # num が現在の node の値よりも小さい場合は左側に追加する
    if node.value > val
      if node.left != nil
        insertNode(val, node.left)
      else
        node.setLeft( createNewNode( val ) )
      end
    # num が現在の node の値以上の場合は右側に追加する
    else
      if node.right != nil
        insertNode(val, node.right)
      else
        node.setRight( createNewNode( val ) )
      end
    end
  end
  
  # ----- ノードの検索
  def findValue( val, node = @treeRoot )
    # 自node よりも小さい値なら左側を検索
    if node.value > val
      if node.left == nil
        return nil
      end
      return findValue( val, node.left )
    end
    # 自node よりも大きい値なら右側を検索
    if node.value < val
      if node.right == nil
        return nil
      end
      return findValue( val, node.right )
    end
    return node
  end
  
  # ----- ノードの削除
  def deleteNode( val )
    node = @treeRoot
    parentNode = nil
    direction = 0
    
    # 削除すべき対象を発見する
    while (node != nil && node.value != val)
      if node.value > val
        parentNode = node
        node = node.left
        direction = -1
      else
        parentNode = node
        node = node.right
        direction = 1
      end
    end
    
    if node == nil
      return false
    end
    
    if (node.left == nil || node.right == nil)
      if node.left == nil
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.right )
        elsif direction == 1
          parentNode.setRight( node.right )
        elsif direciton == 0
          @treeRoot = node.right
        end
      else
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.left )
        elsif direction == 1
          parentNode.setRight( node.left )
        elsif direction == 0
          @treeRoot = node.left
        end
      end
    else
      # node の左側の最も大きい値を取得する
      leftBiggest = node.left
      parentNode = node
      direction = -1
      while leftBiggest.right != nil
        parentNode = leftBiggest
        leftBiggest = leftBiggest.right
        direction = -1
      end
      
      # node の入れ替え
      node.setValue( leftBiggest.value )
      
      if direction == -1
        parentNode.setLeft( leftBiggest.left )
      else
        parentNode.setRight( leftBiggest.left )
      end
    end
    return true
  end
  
  def printNode( depth, node = @treeRoot )
    if node == nil
      return
    end
    printNode( depth + 1, node.left )
    i = 0
    while i < depth
      printf "   "
      i += 1
    end
    printf "#{node.value}¥n"
    printNode( depth + 1, node.right )
  end
  
  private
  # ノードを生成する
  def createNewNode( val )
    newNode = Node.new( val )
    newNode.setLeft( nil )
    newNode.setRight( nil )
    return newNode
  end
end


# Main 
tree = TreeNode.new
action = nil

while action != 0
  tree.printNode( 0 )
  
  puts "実行する操作のタイプを入力して下さい"
  puts "1:追加  2:検索  3:削除  0:終了 >"
  action = gets.chomp.to_i
  
  case action
    when 1
      puts "1〜100までの範囲で追加する数値を入力して下さい"
      i = gets.chomp.to_i
      if (i < 1 || i > 100)
        continue
      end
      tree.insertNode( i )
    when 2
      puts "検索する数字を入力して下さい"
      i = gets.chomp.to_i
      if tree.findValue( i ) != nil
        puts "#{i}を発見しました"
      else
        puts "#{i}は見つかりませんでした"
      end
    when 3
      puts "削除する数字を入力して下さい"
      i = gets.chomp.to_i
      if tree.deleteNode(i)
        puts "#{i}を削除しました"
      else
        puts "#{i}は見つかりませんでした"
      end
  end
end
文字列の検索 (List6-5)
# Node Class
class Node
  attr_reader :value, :key, :left, :right
  
  def initialize( num, val )
    @value = val    # このノードが保持する値
    @key = num      # このノードのキー
    @left = nil     # 左側のノード
    @right = nil    # 右側のノード
  end
  
  # ノードのキーを設定
  def setKey( num )
    @key = key
  end
  
  # ノードの値を設定
  def setValue( val )
    @value = val
  end

  # 左側のノードの設定
  def setLeft( node )
    @left = node
  end
  
  # 右側のノードの設定
  def setRight( node )
    @right = node
  end
end


# Tree Class
class TreeNode
  attr_reader :treeRoot
  
  def initialize()
    @treeRoot = nil    # ツリーのルート
  end
  
  # ----- ノードの追加
  def insertNode( key, val, node = @treeRoot )
    if node == nil
      @treeRoot = createNewNode( key, val )
      return
    end
    
    if node.key > key
      if node.left != nil
        insertNode( key, val, node.left )
      else
        node.setLeft( createNewNode( key, val ) )
      end
    else
      if node.right != nil
        insertNode( key, val, node.right )
      else
        node.setRight( createNewNode( key, val ) )
      end
    end
  end
  
  # ----- ノードの検索
  def findValue( key, node = @treeRoot )
    # 自node よりも小さい値なら左側を検索
    if node.key > key
      if node.left == nil
        return nil
      end
      return findValue( key, node.left )
    end
    # 自node よりも大きい値なら右側を検索
    if node.key < key
      if node.right == nil
        return nil
      end
      return findValue( key, node.right )
    end
    return node
  end
  
  # ----- ノードの削除
  def deleteNode( key )
    node = @treeRoot
    parentNode = nil
    direction = 0
    
    # 削除すべき対象を発見する
    while (node != nil && node.key != key)
      if node.key > key
        parentNode = node
        node = node.left
        direction = -1
      else
        parentNode = node
        node = node.right
        direction = 1
      end
    end
    
    if node == nil
      return false
    end
    
    if (node.left == nil || node.right == nil)
      if node.left == nil
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.right )
        elsif direction == 1
          parentNode.setRight( node.right )
        elsif direction == 0
          @treeRoot = node.right
        end
      else
        if direction == -1
          # 親のポインタを変更する
          parentNode.setLeft( node.left )
        elsif direction == 1
          parentNode.setRight( node.left )
        elsif direction == 0
          @treeRoot = node.left
        end
      end
    else
      # node の左側の最も大きい値を取得する
      leftBiggest = node.left
      parentNode = node
      direction = -1
      while leftBiggest.right != nil
        parentNode = leftBiggest
        leftBiggest = leftBiggest.right
        direction = -1
      end
      
      # node の入れ替え
      node.setValue( leftBiggest.value )
      
      if direction == -1
        parentNode.setLeft( leftBiggest.left )
      else
        parentNode.setRight( leftBiggest.left )
      end
    end
    return true
  end
  
  def printNode( depth, node = @treeRoot )
    if node == nil
      return
    end
    printNode( depth + 1, node.left )
    i = 0
    while i < depth
      printf "   "
      i += 1
    end
    printf "#{node.key} : #{node.value}¥n"
    printNode( depth + 1, node.right )
  end
  
  private
  # ノードを生成する
  def createNewNode( key, val )
    newNode = Node.new( key, val )
    newNode.setLeft( nil )
    newNode.setRight( nil )
    return newNode
  end
end


# Main 
tree = TreeNode.new
action = nil

while action != 0
  tree.printNode( 0 )
  
  puts "実行する操作のタイプを入力して下さい"
  puts "1:追加  2:検索  3:削除  0:終了 >"
  action = gets.chomp.to_i
  
  case action
    when 1
      puts "追加するキー(数値)を入力して下さい"
      key = gets.chomp.to_i
      puts "対応する文字列を入力してください"
      value = gets.chomp
      tree.insertNode( key, value )
    when 2
      puts "検索するキー(数字)を入力して下さい"
      i = gets.chomp.to_i
      found = tree.findValue( i )
      if found != nil
        puts "#{found.value}を発見しました"
      else
        puts "#{i}は見つかりませんでした"
      end
    when 3
      puts "削除するキー(数字)を入力して下さい"
      i = gets.chomp.to_i
      if tree.deleteNode(i)
        puts "#{i}を削除しました"
      else
        puts "#{i}は見つかりませんでした"
      end
  end
end

Home / 別館 Home / Ruby / アルゴリズム / ツリー構造
| 掲示板 | Mail |

【参考書籍】
 紀平拓男、春日伸弥 『プログラミングの宝箱 アルゴリズムとデータ構造』(ソフトバンク パブリッシング 2003)
 参考URL:http://www.cmagazine.jp/books/takarabako/

とみくら まさや(vzx01036@nifty.ne.jp) $ Date: 2004/02/04 $