Refactor build_tree into a method

This commit is contained in:
sweeneyde 2020-08-04 07:46:17 -04:00
parent d79148a0c1
commit 57427ea2cc
1 changed files with 31 additions and 29 deletions

View File

@ -311,6 +311,7 @@ def _siftup_max(heap, pos):
heap[pos] = newitem
_siftdown_max(heap, startpos, pos)
class _MergeNode:
"""
Binary tree invariants:
@ -362,6 +363,30 @@ class _MergeNode:
left.parent = right.parent = node
return node
@classmethod
def build_tree(cls, iterables, key, reverse):
nodes = []
for it in iterables:
leaf = cls.construct_leaf(it, key)
if leaf is not None:
nodes.append(leaf)
if not nodes:
return None
n = len(nodes)
# unite pairs of adjacent nodes with a common parent until all
# nodes are united into one big tree.
while n > 1:
# Prefer keeping the leftmost nodes shallower in the tree
# since they're more likely to win for stability reasons.
new_nodes, rest = nodes[:n & 1], nodes[n & 1:]
for left, right in zip(rest[::2], rest[1::2]):
parent = cls.construct_parent(left, right, reverse)
new_nodes.append(parent)
nodes = new_nodes
n = len(nodes)
(root,) = nodes
return root
def promote_sibling(self):
"""
Remove self and its sibling from the tree, while linking their
@ -369,9 +394,6 @@ class _MergeNode:
"""
assert self.leaf is self
parent = self.parent
# This can't be the last node because we switch to the
# fast yield-from case when we reach the last node.
assert parent is not None
left = parent.left
right = parent.right
sibling = left if self is right else right
@ -406,33 +428,14 @@ def merge(*iterables, key=None, reverse=False):
'''
nodes = []
for it in iterables:
leaf = _MergeNode.construct_leaf(it, key)
if leaf is not None:
nodes.append(leaf)
if not nodes:
root = _MergeNode.build_tree(iterables, key, reverse)
if root is None:
return
n = len(nodes)
if n == 1:
(root,) = nodes
elif root.leaf is root:
yield root.left
yield from root.right
return
# unite pairs of adjacent nodes with a common parent until all nodes
# are united into one big tree.
while n > 1:
# Prefer keeping the leftmost nodes shallower in the tree
# since they're more likely to win for stability reasons.
new_nodes, rest = nodes[:n & 1], nodes[n & 1:]
for left, right in zip(rest[::2], rest[1::2]):
parent = _MergeNode.construct_parent(left, right, reverse)
new_nodes.append(parent)
nodes = new_nodes
n = len(nodes)
(root,) = nodes
_next, _StopIteration = next, StopIteration
while True:
# To find the value to yield, check which leaf
@ -445,10 +448,9 @@ def merge(*iterables, key=None, reverse=False):
node.left = val = _next(node.right)
except _StopIteration:
node = node.promote_sibling()
if node.leaf is root:
assert node is root
yield node.left
yield from node.right
if root.leaf is root:
yield root.left
yield from root.right
return
else:
node.key = val if key is None else key(val)