我们希望能够在屏幕上打印出树形的二叉树。二叉树以数组的形式存在。就像下面这样
我们为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

3

4

5

6

7

8

9

10

11

12

13

14

15

16


17

18

19

此后我们的所有操作都将针对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
(parent
i).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

3

4

5

6


7

8

9

10


11

12

13

14


15

16

17

18

19

20

21

22


23

24

25

26

27

28

29

30

31

程序到此结束。
缺陷
由于我们是从下到上计算每个元素的宽度,所以如果A[2]超长的话,就会变成下面这样
如果是你,会怎么解决这个问题呢?
全部源代码
全部源代码和单元测试代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!