-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTreeNode.py
141 lines (126 loc) · 4.55 KB
/
TreeNode.py
1
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
from functools import cache
def int_pow(val: int, power:int) -> int:
"""Fast power that """
if power < 0:
return 0
assert power >= 0
return int(val ** power + 0.0001)
@cache
def double_plus_one(val):
"""Recursive cached function use for laying out grides"""
assert val >= 0
if val == 0:
return 0
return 2 * double_plus_one(val - 1) + 1
class TreeNode(object):
"""Node implementation for binary trees"""
def __init__(self, val):
self._value = val
self.left = None
self.right = None
self.height = 1
def stringify(self, maxWidth=2):
grid = TreeNode.add_node_to_grid(self)
return TreeNode.stringify_grid(grid, maxWidth)
def __str__(self):
return self.stringify()
@property
def value(self):
# in derived classes, we may want to override this
return self._value
@staticmethod
def add_node_to_grid(node, grid=None, yPos=0, xPos=0):
"""Used to print out tree at end"""
if grid is None:
grid = {}
grid.setdefault(yPos, {})
grid[yPos][xPos] = node
if node.left is not None:
# are we positive or negative?
newXpos = -1 # only true in root case
if xPos < 0:
newXpos = 2 * xPos
elif xPos > 0:
newXpos = 2 * xPos - 1
TreeNode.add_node_to_grid(node.left, grid, yPos + 1, newXpos)
if node.right is not None:
# are we positive or negative?
newXpos = 1 # only true in root case
if xPos < 0:
newXpos = 2 * xPos + 1
elif xPos > 0:
newXpos = 2 * xPos
TreeNode.add_node_to_grid(node.right, grid, yPos + 1, newXpos)
return grid
@staticmethod
def simple_stringify_grid(grid):
"""Diagnostic stringification to make sure grid setup correctly."""
retStr = ''
for layer, row in sorted(grid.items()):
retStr += f'{layer} : '
for xPos, node in sorted(row.items()):
nodeVal = f'{node.value:2}'
extra = ''
if node.left:
extra += f'L{node.left.value:2}'
if node.right:
if extra:
extra += ' '
extra += f'R{node.right.value:2}'
if extra:
nodeVal += f' ({extra})'
retStr += f'{xPos:3} {nodeVal}, '
retStr += '\n'
return retStr
@staticmethod
def stringify_grid(grid, maxWidth=2, verbose=False):
# how many layers deep is this grid
deep = len(grid)
fmtStr = '{:<%d}' % maxWidth
retStr = ''
empty = ' ' * maxWidth
left = fmtStr.format(' ' * ( maxWidth // 2) + '/' )
right = fmtStr.format(' ' * ((maxWidth - 0) // 2) + '\\')
for layer, row in sorted(grid.items()):
# should be 0 for last row
fromBot = deep - layer - 1
if fromBot:
before = int_pow(2, fromBot ) - 1
between = int_pow(2, fromBot + 1) - 1
midSlsh = int_pow(2, fromBot ) - 2
aftSlsh = befSlsh = double_plus_one(fromBot) + 1
befSlsh = double_plus_one(fromBot - 1)
else:
before = 0
between = 1
# won't get used
midSlsh = 0
aftSlsh = 0
befSlsh = 0
## if layer == 1:
## between = before
numSlsh = int_pow(2, layer)
maxPos = int_pow(2, layer - 1)
if verbose:
print(f'{layer=} {maxPos=} {deep=} {fromBot=} {before=:2} ' +
f'{between=:2} {befSlsh=} {midSlsh=:2} {aftSlsh=:2}')
if before:
retStr += empty * before
for pos in range(-maxPos, maxPos + 1):
node = row.get(pos)
if node is None and not pos:
continue
if node:
value = node.value
else:
value = ''
retStr += fmtStr.format(value) + empty * between
retStr += '\n'
if not fromBot:
# don't put toothpicks on bottom row
break
retStr += empty * befSlsh
for _ in range(numSlsh):
retStr += left + empty * midSlsh + right + empty * aftSlsh
retStr += '\n'
return retStr