在Ruby中实现二叉树

7

我一直在尝试在Ruby中实现BinaryTree类,但是我遇到了“stack level too deep”错误,尽管我似乎没有在那段特定的代码中使用任何递归:

1.  class BinaryTree
2.    include Enumerable
3.      
4.      attr_accessor :value
5.      
6.      def initialize( value = nil )
7.          @value = value
8.          @left = BinaryTree.new  # stack level too deep here
9.          @right = BinaryTree.new # and here
10.     end
11.     
12.     def empty?
13.         ( self.value == nil ) ? true : false
14.     end
15.         
16.         def <<( value )
17.           return self.value = value if self.empty?
18. 
19.           test = self.value <=> value
20.           case test
21.             when -1, 0 
22.                 self.right << value
23.             when 1 
24.                 self.left << value
25.           end
26.         end     # <<
27.     
28.  end

编辑:我的问题有些偏离了轨道。当前的代码设置在第8行给出了“堆栈层级太深”的错误。然而,如果我使用Ed S.的解决方案

@left = @right = nil

然后<<方法会出现错误:在第22行,undefined method '<<' for nil:NilClass (NoMethodError)。请问有谁能提供解决方法?我的想法是如果我可以告诉BinaryTree类变量leftrightBinaryTree实例(即它们的类型为BinaryTree),那么问题就可以得到解决。我的想法对吗?

2
每次调用BinaryTree.new,它都会触发initialize方法并调用另一个BinaryTree.new,然后无限循环重复。这就是为什么你的堆栈溢出了。 - bigpotato
4个回答

13

虽然在那段代码中似乎没有使用递归:

不过......

def initialize( value = nil )
    @value = value
    @left = BinaryTree.new  # this calls initialize again
    @right = BinaryTree.new # and so does this, but you never get there
end

这是无限递归。你调用了initialize,然后又调用了new,接着又调用了initialize...如此循环。

你需要添加一个保护机制来检测是否已经初始化了主节点并正在初始化叶节点,在这种情况下,@left@right 应该被设置为nil

def initialize( value=nil, is_sub_node=false )
    @value = value
    @left = is_sub_node ? nil : BinaryTree.new(nil, true)
    @right = is_sub_node ? nil : BinaryTree.new(nil, true)
end
说实话...为什么你不直接将左右节点初始化为nil呢?它们还没有值,你能得到什么好处呢?从语义上讲更合理;你创建了一个只有一个元素的新列表,也就是说左右元素尚不存在。我会直接使用以下代码:
def initialize(value=nil)
    @value = value
    @left = @right = nil
end

@Maputo:是的,你需要定义一个名为initialize的方法,当有人调用YourType.new时就会调用这个方法。你只需将左右两侧设置为nil即可。这样更有意义,而且解决了无限递归的问题。 - Ed S.
@Maputo:好的,所以你的代码又出现了一个错误,这并不意味着你最初的方法是正确的。你从来没有赋值 value,你只是一遍又一遍地递归调用那个方法。如果你要使用递归,你需要一个保护条件来确定何时停止,即何时赋值 value并退出该过程。 - Ed S.
我在 Ruby 中不明白的是如何创建一个具有 BinaryTree 属性的类 BinaryTree(例如)。 - Maputo
其实,我认为是这样的。<< 方法的第一行在遇到空叶子时立即返回该值。或者至少我认为是这样的... - Maputo
1
@Maputo:你可以将它们初始化为“nil”。在Ruby中,变量没有类型,只有值有类型。 - mu is too short
显示剩余4条评论

2
1.  class BinaryTree
2.    include Enumerable
3.      
4.      attr_accessor :value
5.      
6.      def initialize( value = nil )
7.          @value = value
8.      end 
9.      
10.     def each # visit
11.         return if self.nil?
12.         
13.         yield self.value
14.         self.left.each( &block ) if self.left
15.         self.right.each( &block ) if self.right     
16.     end
17. 
18.     def empty?
19.         # code here
20.     end
21.         
22.     def <<( value ) # insert
23.         return self.value = value if self.value == nil
24. 
25.         test = self.value <=> value
26.         case test
27.             when -1, 0
28.                 @right = BinaryTree.new if self.value == nil
29.                 self.right << value
30.             when 1 
31.                 @left = BinaryTree.new if self.value == nil
32.                 self.left << value
33.         end
34.     end     # <<
35.  end

1
你的代码可能需要修复无限递归的问题。这里提供了一个二叉树的工作示例。你需要有一个基本条件来终止你的递归,否则它将成为无限深度的堆栈。
自引用数据结构示例 - 二叉树
class TreeNode
  attr_accessor :value, :left, :right

  # The Tree node contains a value, and a pointer to two children - left and right 
  # Values lesser than this node will be inserted on its left
  # Values greater than it will be inserted on its right
  def initialize val, left, right
    @value = val
    @left = left
    @right = right
  end
end

class BinarySearchTree

  # Initialize the Root Node
  def initialize val
    puts "Initializing with: " + val.to_s
    @root = TreeNode.new(val, nil, nil)
  end

  # Pre-Order Traversal
  def preOrderTraversal(node = @root)
    return if (node == nil)
    preOrderTraversal(node.left)
    preOrderTraversal(node.right)
    puts node.value.to_s
  end

  # Post-Order Traversal
  def postOrderTraversal(node = @root)
    return if (node == nil)
    puts node.value.to_s
    postOrderTraversal(node.left)
    postOrderTraversal(node.right)
  end

  # In-Order Traversal : Displays the final output in sorted order
  # Display smaller children first (by going left)
  # Then display the value in the current node 
  # Then display the larger children on the right
  def inOrderTraversal(node = @root)
    return if (node == nil)
    inOrderTraversal(node.left)
    puts node.value.to_s
    inOrderTraversal(node.right)
  end

  # Inserting a value
  # When value > current node, go towards the right
  # when value < current node, go towards the left
  # when you hit a nil node, it means, the new node should be created there
  # Duplicate values are not inserted in the tree
  def insert(value)
    puts "Inserting :" + value.to_s
    current_node = @root
    while nil != current_node
      if (value < current_node.value) && (current_node.left == nil)
        current_node.left = TreeNode.new(value, nil, nil)
      elsif (value > current_node.value) && (current_node.right == nil)
        current_node.right = TreeNode.new(value, nil, nil)
      elsif (value < current_node.value)
        current_node = current_node.left
      elsif (value > current_node.value)
        current_node = current_node.right
      else
        return
      end
    end
  end
end

bst = BinarySearchTree.new(10)
bst.insert(11)
bst.insert(9)
bst.insert(5)
bst.insert(7)
bst.insert(18)
bst.insert(17)
# Demonstrating Different Kinds of Traversals
puts "In-Order Traversal:"
bst.inOrderTraversal
puts "Pre-Order Traversal:"
bst.preOrderTraversal
puts "Post-Order Traversal:"
bst.postOrderTraversal

=begin

   Output :
     Initializing with: 10
   Inserting :11
   Inserting :9
   Inserting :5
   Inserting :7
   Inserting :18
   Inserting :17
   In-Order Traversal:
     5
   7
   9
   10
   11
   17
   18
   Pre-Order Traversal:
     7
   5
   9
   17
   18
   11
   10
   Post-Order Traversal:
     10
   9
   5
   7
   11
   18
   17

   =end

参考资料:http://www.thelearningpoint.net/computer-science/basic-data-structures-in-ruby---binary-search-tre


0

@pranshantb1984 - 你提供的参考资料很好,但我认为代码需要做出一些小改变。需要按照以下方式更新PreOrder和PostOrder代码

# Post-Order Traversal
def postOrderTraversal(node= @root)
    return if (node == nil)
    postOrderTraversal(node.left)
    postOrderTraversal(node.right)
    puts node.value.to_s
end 

# Pre-Order Traversal
def preOrderTraversal(node = @root)
    return if (node == nil)
    puts node.value.to_s
    preOrderTraversal(node.left)
    preOrderTraversal(node.right)
end

前序遍历

10 -> 9 -> 5 -> 7 -> 11 -> 18 -> 17

后序遍历

7 -> 5 -> 9 -> 17 -> 18 -> 11 -> 10


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接