2016-04-27 86 views
0

我有這個類:丟失參考

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