我们希望能够在屏幕上打印出树形的二叉树。二叉树以数组的形式存在。就像下面这样
我们为Array类增加了一个 heap_string 函数,它返回数组的二叉树结构。下面将描述这个函数的实现方法。
实现方法
我们的想法是,先在数组A的每个元素的前后增加若干个空格,然后再在适当的地方换行,就可以得到一棵树了。如下图所示
其规律就是,A[2]的宽度=A[4]的宽度+A[5]的宽度;A[1]的宽度=A[2]的宽度+A[3]的宽度。A[3]的宽度不太好办,因为它的下方没有孩子节点。这个问题可以通过向数组A增加若干个值为空格的节点,将数组变成一棵满二叉树来解决。
构造一棵满二叉树
我们将创建数组A的一个副本“out_heap”,它的元素都是String,并且是一棵满二叉树,ruby代码如下
1
class
Array
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5 out_heap = Array.new
6 out_heap.base_index = 1
7
8 self.each do | v |
9 out_heap << v.to_s
10 end
11
12 full_tree_size = 2 ** (self.heap_height + 1 ) - 1
13 out_heap += Array.new(full_tree_size - out_heap.length, ' ' )
14 out_heap.heap_size = self.heap_size
15
16
17
18 end
19 end
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5 out_heap = Array.new
6 out_heap.base_index = 1
7
8 self.each do | v |
9 out_heap << v.to_s
10 end
11
12 full_tree_size = 2 ** (self.heap_height + 1 ) - 1
13 out_heap += Array.new(full_tree_size - out_heap.length, ' ' )
14 out_heap.heap_size = self.heap_size
15
16
17
18 end
19 end
此后我们的所有操作都将针对out_heap。接下来,我们将为out_heap中的每个元素增加若干个空格。
追加空格
我们将以“左-右-中”的顺序遍历out_heap,即以out_heap[4]->out_heap[5]->out_heap[2]->out_heap[3]->out_heap[1]的顺序访问out_heap,依次计算出每个元素的宽度并插入空格。详细描述如下:
1. 如果是叶子,则在叶子的左右添加space。space默认等于两个空格。
2. 如果不是叶子,则节点的宽度等于两个孩子的宽度之和,并且居中显示。
对应的ruby代码如下
1
class
Array
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9 out_heap.extend(HeapLRMEnumerator)
10 out_heap.each_index( 1 , out_heap.length) do | i |
11 l = left(i)
12 r = right(i)
13
14 if l > out_heap.length # is leaf
15 out_heap[i] = space + out_heap[i] + space
16 elsif l <= out_heap.length # is not leaf
17 out_heap[i] = out_heap[i].center(out_heap[l].length + out_heap[r].length)
18 end
19 end
20
21
22
23 end
24 end
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9 out_heap.extend(HeapLRMEnumerator)
10 out_heap.each_index( 1 , out_heap.length) do | i |
11 l = left(i)
12 r = right(i)
13
14 if l > out_heap.length # is leaf
15 out_heap[i] = space + out_heap[i] + space
16 elsif l <= out_heap.length # is not leaf
17 out_heap[i] = out_heap[i].center(out_heap[l].length + out_heap[r].length)
18 end
19 end
20
21
22
23 end
24 end
到此out_heap中的值为 [" A[1] ", " A[2] ", " A[3] ", " A[4] ", " A[5] ", " ", " "]
使用“左-右-中”的顺序遍历数组是通过上面代码的第9行“out_heap.extend(HeapLRMEnumerator)”来实现的,HeapLRMEnumerator 是我们编写的一个以“左-右-中”的顺序遍历数组的迭代器,代码将在本文的最后给出。
生成连接线“/”和“\”
我们还需要生成节点之间的连接线。注意到连接线的宽度总是和父节点的宽度相同,但是我们不能象上面那样使用"/\".center(宽度)这样的语句,因为我们需要的不仅仅是居中,而是“分散剧中”---我们需要的是
“ / \ ”而不是“ /\ ”。为此我们为String类追加了一个distribute_center()函数,可以实现这个效果(源代码在最后)。我们为out_heap中每个有孩子的节点生成连接线,保存到conn_lines数组中,ruby代码如下
1
class
Array
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9
10
11
12 # Generates "/" and "\"
13 out_heap = out_heap.dup # back to ordered enumerator
14 out_heap.base_index = 1
15
16 conn_lines = Array.new
17 ( 1 ..out_heap.length).each do | i |
18 l = left(i)
19 r = right(i)
20 conn_line_string = ""
21 if l <= out_heap.heap_size # has left child
22 conn_line_string << " / "
23 end
24 if r <= out_heap.heap_size # has right chile
25 conn_line_string << " \\ "
26 end
27 conn_lines[i] = conn_line_string.distribute_center(out_heap[i].length)
28 end
29
30
31 end
32 end
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9
10
11
12 # Generates "/" and "\"
13 out_heap = out_heap.dup # back to ordered enumerator
14 out_heap.base_index = 1
15
16 conn_lines = Array.new
17 ( 1 ..out_heap.length).each do | i |
18 l = left(i)
19 r = right(i)
20 conn_line_string = ""
21 if l <= out_heap.heap_size # has left child
22 conn_line_string << " / "
23 end
24 if r <= out_heap.heap_size # has right chile
25 conn_line_string << " \\ "
26 end
27 conn_lines[i] = conn_line_string.distribute_center(out_heap[i].length)
28 end
29
30
31 end
32 end
到此 conn_lines 中的内容为[nil, " / \\ ", " / \\ ", " ", " ", " ", " ", " "]
合并
接下来我们把上面生成的所有的东西合并在一起,并加上换行符。
1
class
Array
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9
10
11
12 # Generates "/" and "\"
13
14
15
16 # Combine out_heap and conn_lines
17 out_heap.each_index do | i |
18 parent = parent(i)
19
20 if parent >= 1 && leftest?(i) # is leftest and is not root
21 conn_lines_string = ""
22 (parenti).each do | j |
23 conn_lines_string << (conn_lines[j] || "" )
24 end
25
26 out_heap[i] = " \n " + conn_lines_string + " \n " + out_heap[i]
27 end
28 end
29
30 return out_heap.to_s
31 end
2 def heap_string(space = ' ' )
3 # Prepares out_heap
4 # The out_heap is a full tree and every node is a String
5
6
7
8 # Adds spaces to each node in out_heap
9
10
11
12 # Generates "/" and "\"
13
14
15
16 # Combine out_heap and conn_lines
17 out_heap.each_index do | i |
18 parent = parent(i)
19
20 if parent >= 1 && leftest?(i) # is leftest and is not root
21 conn_lines_string = ""
22 (parenti).each do | j |
23 conn_lines_string << (conn_lines[j] || "" )
24 end
25
26 out_heap[i] = " \n " + conn_lines_string + " \n " + out_heap[i]
27 end
28 end
29
30 return out_heap.to_s
31 end
程序到此结束。
缺陷
由于我们是从下到上计算每个元素的宽度,所以如果A[2]超长的话,就会变成下面这样
如果是你,会怎么解决这个问题呢?
全部源代码
全部源代码和单元测试代码