丢失AVL树中的引用

时间:2016-04-27 22:13:16

标签: ruby

我有这堂课:

class AVLTree
  class Node
    attr_accessor :value, :height
    attr_accessor :left, :right

    def initialize(value, height)
      @value = value
      @height = height
    end
  end

  attr_accessor :root

  def initialize
    @root = Node.new(nil, 0)
  end

  # Right rotation of the tree
  def right_rotation(node = @root)
    begin
      root = node.left
      node.left = root.right
      root.height = node.height
      root.right = node
      update_subtrees_height(root.right, root.height)
      update_subtrees_height(root.left, root.height)
    rescue Exception => e
      puts "Tree not able to do a right rotation: #{e.message}"
      puts e.backtrace.inspect
    end
    root
  end

  # Left rotation of the tree
  def left_rotation(node = @root)
    begin
      root = node.right
      node.right = root.left
      root.height = node.height
      root.left = node
      update_subtrees_height(root.right, root.height)
      update_subtrees_height(root.left, root.height)
    rescue Exception => e
      puts "Tree not able to do a left rotation: #{e.message}"
      puts e.backtrace.inspect
    end
    root
  end

  # Update the height of the elements of a sub-tree and all other sub-tree of its side
  def update_subtrees_height(node, height)
    return if node.nil?
    node.height = height + 1
    update_subtrees_height(node.left, node.height)
    update_subtrees_height(node.right, node.height)
  end

  def balance_factor(node = @root)
    leftSize = node.left.nil? ? 0 : deepest_node(node.left).height
    rightSize = node.right.nil? ? 0 : deepest_node(node.right).height
    rightSize - leftSize
  end

  def balance(node = @root)
    balanceFactor = balance_factor(node)
    return node if balanceFactor <= 1 && balanceFactor >= -1
    if balanceFactor > 1
      if balance_factor(node.right) < 0
        node.right = right_rotation(node.right)
        node = left_rotation(node)
      else
        node = left_rotation(node)
      end
    else
      if balance_factor(node.left) > 0
        node.left = right_rotation(node.left)
        node = left_rotation(node)
      else
        node = right_rotation(node)
      end
    end
  end

  def print_tree(node = @root)
    return if node.nil?
    puts "#{node.left.nil? ? 'null' : node.left.value} - #{node.nil? ? 'null' : node.value}(L#{node.height}) - #{node.right.nil? ? 'null' : node.right.value}"
    print_tree(node.left)
    print_tree(node.right)
  end

  def deepest_node(node = @root, deepest = @root)
    return deepest if node.nil?
    if node.height > deepest.height then deepest = node else deepest end
    right = deepest_node(node.left, deepest)
    left = deepest_node(node.right, deepest)
    right.height > left.height ? right : left
  end

  def insert(value, node = @root)
    case value <=> node.value
    when -1
      if node.left.nil?
        node.left = Node.new(value, node.height + 1)
      else
        insert(value, node.left)
        node = balance(node.left)
      end
    when 1
      if node.right.nil?
        node.right = Node.new(value, node.height + 1)
      else
        insert(value, node.right)
        node = balance(node)
      end
    else
      node.value = value
    end
  end

end

我正在做这个测试:

a = AVLTree.new

[1,2,3,4,5].each do |v|
  a.insert v
end

a.print_tree a.root

我的预期输出是

   1 - 2(H0) - 4
null - 1(H1) - null
   3 - 4(H1) - 5
null - 3(H2) - null
null - 5(H2) - null

谁代表下面的树:

     2
    / \
   1   4
      / \
     3   5

我的输出是:

null - 1(H2) - null

调试代码,每次调用行insert时,我都会在node = balance(node.left)方法中发现问题。这时我丢失了树的引用。

balance方法在我的测试中没问题。例如:

当我在树上插入3时,我有

  1
   \
    2
     \
      3

然后我用节点1调用余额,接收预期的树

   2
  / \
 1   3

我认为的逻辑是:每当我在树中插入一个值时,我会检查其平衡并在必要时修复它。使用我的insert方法的递归,我将向上检查树,从插入的节点开始上面一点。在上面的示例中,当我转到节点1并对其进行平衡时,balance方法将返回一个新节点,其中2与此子树的根一样,旧节点接收该节点。

会发生这种情况,但由于某种原因,AVL树的根节点丢失了对其他节点的引用。任何想法我怎么解决这个问题?

1 个答案:

答案 0 :(得分:0)

我还不确定我的问题的原因,但是我以相信更好的方式重新实现了AVL树。它起作用了,我的主要教训是:如果你正在实现一个树,那么创建在节点类本身中操作节点(插入,更新等)的方法是明智的选择!这听起来很明显,但在你第一次这样做时并不那么明显。

以下是我班级的新版本:

class AVLTree
  class Node
    attr_accessor :right, :left
    attr_accessor :key, :height

    def initialize(key = nil, height = nil)
      @key = key
      @height = height
      @left = @right = EmptyNode.new
    end

    def insert(key)
      case key <=> @key
      when -1
        @left = @left.insert(key)
        @left.height = self.height + 1
      when 0
        @value = value
      when 1
        @right = @right.insert(key)
        @right.height = self.height + 1
      else
        raise TypeError, "Cannot compare #{key} with #{@key}"
      end
      balance
    end

    def balance
      balanceFactor = balance_factor
      return self if balanceFactor >= -1 && balanceFactor <= 1
      if balanceFactor > 1
        if @right.balance_factor < 0
          @right = @right.rotate_right
          root = rotate_left
        else
          root = rotate_left
        end
      else
        if @left.balance_factor > 0
          @left = @left.rotate_left
          root = rotate_right
        else
          root = rotate_right
        end
      end
      root
    end

    def balance_factor
      leftSize = @left.nil? ? 0 : deepest_node(@left).height
      rightSize = @right.nil? ? 0 : deepest_node(@right).height
      rightSize - leftSize
    end

    def deepest_node(node = self, deepest = self)
      return deepest if node.nil?
      if node.height > deepest.height then deepest = node else deepest end
      right = deepest_node(node.left, deepest)
      left = deepest_node(node.right, deepest)
      right.height > left.height ? right : left
    end

    def rotate_left
      raise "The node have not right child to do a left rotation" if @right.key.nil?
      root = @right
      @right = root.left
      root.height = @height
      root.left = self
      root.left.update_height(1)
      root.right.update_height(-1)
      root
    end

    def rotate_right
      root = @left
      @left = root.right
      root.height = @height
      root.right = self
      root.left.update_height(-1)
      root.right.update_height(1)
      root
    end

    def update_height(value)
      @height += value
      @left.update_height(value)
      @right.update_height(value)
    end

    class EmptyNode < Node
      def initialize
        @key = nil
        @height = 0
      end

      def insert(key)
        Node.new(key, 0)
      end

      def update_height(value)
      end
    end
  end

  attr_accessor :root

  def initialize
    @root = Node::EmptyNode.new
  end

  def insert(key)
    @root = @root.insert(key)
  end

  def print_tree(node = @root)
    return if node.key.nil?
    puts "#{node.left.nil? || node.left.key.nil? ? 'null' : node.left.key} - #{node.nil? || node.key.nil? ? 'null' : node.key}(L#{node.height}) - #{node.right.nil? || node.right.key.nil? ? 'null' : node.right.key}"
    print_tree(node.left)
    print_tree(node.right)
  end

  # sequence = left, root and right of each sub-tree
  def in_order(node = @root, sequence = [])
    return sequence if node.key.nil?
    in_order(node.left, sequence)
    sequence << [node.key, node.height]
    in_order(node.right, sequence)
  end
end