Skip to content

Commit ddb7bb9

Browse files
author
Philip Guo
committed
added
1 parent 15347f8 commit ddb7bb9

1 file changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# TODO: how do you set the BACKGROUND COLOR of a GraphViz node ... fill=?
2+
3+
# TODO: make this work in both Python 2 and 3
4+
5+
from collections import defaultdict
6+
7+
ID = 0
8+
9+
class TreeNode:
10+
def __init__(self, dat):
11+
self.data = str(dat) # convert to string for easy display in GraphViz
12+
self.parent = None
13+
self.left = None
14+
self.right = None
15+
16+
self.__penwidth = 1 # thickness of node border
17+
18+
# HTML-like RGB hex values - e.g., "#bb0000"
19+
self.__color = None # border color
20+
self.__fill = None # internal node color
21+
22+
# assign unique IDs in node creation order
23+
global ID
24+
self.id = 'n' + str(ID)
25+
ID += 1
26+
27+
def set_border_color(self, col):
28+
self.__color = col
29+
30+
def set_fill(self, col):
31+
self.__fill = col
32+
33+
def set_width(self, w):
34+
assert w > 0
35+
self.__penwidth = w
36+
37+
def __str__(self):
38+
ret = '%s[label="%s"' % (self.id, self.data)
39+
if self.__penwidth > 1:
40+
ret += ',penwidth=%d' % self.__penwidth
41+
if self.__color:
42+
ret += ',color="%s"' % self.__color
43+
if self.__fill:
44+
ret += ',fill="%s"' % self.__fill
45+
ret += ']'
46+
return ret
47+
48+
49+
# render a binary tree of TreeNode objects starting at root in a pretty
50+
# GraphViz format using the balanced tree hack from
51+
# http://www.graphviz.org/content/FaqBalanceTree
52+
def graphviz_render(root):
53+
print 'digraph G{'
54+
55+
queue = [] # each element is (node, level #)
56+
57+
# Key: level number
58+
# Value: sorted list of node IDs at that level (including phantom nodes)
59+
nodes_by_level = defaultdict(list)
60+
61+
62+
def print_phantom(parent_id, suffix):
63+
phantom_id = parent_id + '_phantom_' + suffix
64+
print '%s [label="",width=.1,style=invis]' % phantom_id
65+
print '%s->%s [style=invis]' % (parent_id, phantom_id)
66+
return phantom_id
67+
68+
def bfs_visit():
69+
# base case
70+
if not queue:
71+
return
72+
73+
n, level = queue.pop(0)
74+
75+
print n # current node
76+
if n.left or n.right:
77+
if n.left:
78+
print '%s->%s' % (n.id, n.left.id)
79+
queue.append((n.left, level+1))
80+
nodes_by_level[level+1].append(n.left.id)
81+
else:
82+
# insert phantom to make tree look good
83+
ph_id = print_phantom(n.id, 'L')
84+
nodes_by_level[level+1].append(ph_id)
85+
86+
# always insert invisible middle phantom
87+
ph_id = print_phantom(n.id, 'M')
88+
nodes_by_level[level+1].append(ph_id)
89+
90+
if n.right:
91+
print '%s->%s' % (n.id, n.right.id)
92+
queue.append((n.right, level+1))
93+
nodes_by_level[level+1].append(n.right.id)
94+
else:
95+
# insert phantom to make tree look good
96+
ph_id = print_phantom(n.id, 'R')
97+
nodes_by_level[level+1].append(ph_id)
98+
99+
bfs_visit() # recurse!
100+
101+
queue.append((root, 1))
102+
bfs_visit()
103+
104+
# make sure all nodes at the same level are vertically aligned
105+
for level in nodes_by_level:
106+
node_ids = nodes_by_level[level]
107+
if len(node_ids) > 1:
108+
print '{rank=same %s [style=invis]}' % '->'.join(node_ids)
109+
110+
print '}'
111+
112+
113+
if __name__ == "__main__":
114+
# simple test tree
115+
a = TreeNode('a')
116+
117+
b0 = TreeNode('b0')
118+
a.left = b0
119+
120+
b1 = TreeNode('b1')
121+
a.right = b1
122+
123+
c0 = TreeNode('c0')
124+
b0.left = c0
125+
126+
c1 = TreeNode('c1')
127+
b0.right = c1
128+
b0.set_fill('red')
129+
130+
c2 = TreeNode('c2')
131+
b1.left = c2
132+
133+
d1 = TreeNode('d1')
134+
c0.right = d1
135+
136+
d2 = TreeNode('d2')
137+
c2.left = d2
138+
139+
d3 = TreeNode('d3')
140+
c1.left = d3
141+
142+
d4 = TreeNode('d4')
143+
c1.right = d4
144+
145+
graphviz_render(a)
146+
147+
148+
'''
149+
/* balanced tree hack from http://www.graphviz.org/content/FaqBalanceTree */
150+
151+
/*
152+
digraph G {
153+
a -> b0
154+
xb [label="",width=.1,style=invis]
155+
a -> xb [style=invis]
156+
a -> b1
157+
158+
{rank=same b0 -> xb -> b1 [style=invis]}
159+
160+
b0 -> c0
161+
xc [label="",width=.1,style=invis]
162+
b0 -> xc [style=invis]
163+
b0 -> c1
164+
165+
{rank=same c0 -> xc -> c1 [style=invis]}
166+
}
167+
*/
168+
'''

0 commit comments

Comments
 (0)