2016-04-27 4 views
0

У меня есть этот класс:Losing ссылки в дереве AVL

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 

кто represet дерево ниже:

 2 
    /\ 
    1 4 
    /\ 
    3 5 

Вывод, что у меня есть:

null - 1(H2) - null 

отладки кода, я descovered, что проблема возникает в моем insert методе, каждый раз, когда я называю линию node = balance(node.left). В это время я теряю ссылки на дерево.

Метод balance в моих тестах. Например:

Когда я вставить 3 на дереве, у меня есть

1 
    \ 
    2 
    \ 
     3 

, а затем я вызвать баланс с узлом 1, получая ожидаемое дереву

2 
/\ 
1 3 

Логика, Я думал: каждый раз, когда я вставляю значение в дерево, я проверю его баланс и исправлю его, если это необходимо. С рекурсивностью моего метода insert я проверю дерево вверх, начиная с одной точки выше от вставленного узла. В приведенном выше примере, когда я перехожу к узлу 1 и балансирую его, метод balance вернет новый узел с 2, как корень этого поддерева, и мой старый узел получит это.

Это происходит, но по какой-то причине корневой узел дерева AVL потерял ссылки на другие узлы. Любая идеология, как я могу это исправить?

ответ

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 
Смежные вопросы