I am implementing an algorithm to solve the following problem:
Given a sorted (increasing order) array with unique integer elements, write an algorithm to create a binary search tree with minimal height.
The solution of this problem using recursion is quite straightforward:
class Node:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def build_bst_recursive(array):
size = len(array)
return _build_bst(array, first=0, last=size - 1)
def _build_bst(array, first, last):
if last < first:
return None
mid = (first + last) // 2
node = Node(mid)
node.left = _build_bst(array, first, mid - 1)
node.right = _build_bst(array, mid + 1, last)
return node
Out of curiosity I also would like to solve the same problem using an iterative algorithm. Here is what I came up with:
class Stack:
class _Node:
def __init__(self, value):
self.value = value
self.next_node = None
def __init__(self):
self.head_node = None
def is_empty(self):
return self.head_node is None
def push(self, value):
node = self._Node(value)
node.next_node = self.head_node
self.head_node = node
def pop(self):
if self.is_empty():
raise Exception("Stack is empty.")
node = self.head_node
self.head_node = node.next_node
return node.value
def build_bst_iterative(array):
size = len(array)
# Stack stores tuples of the first and the last indices of half-segments:
stack_indices = Stack()
stack_indices.push((0, size - 1))
# Stack stores tree nodes:
stack_nodes = Stack()
# Add to the stack a node that will be a root of the tree:
root_node = Node(None)
stack_nodes.push(root_node)
while not stack_indices.is_empty():
first, last = stack_indices.pop()
node = stack_nodes.pop()
if last < first:
# The segment is degenerated. Keep the node empty.
continue
elif last == first:
# Assign the value, it is the last bottom node.
node.value = array[last]
continue
mid = (first + last) // 2
node.value = array[mid]
node.left = Node(None)
node.right = Node(None)
# Update stacks:
# (right half-segment)
stack_indices.push((mid + 1, last))
stack_nodes.push(node.right)
# (left half-segment)
stack_indices.push((first, mid - 1))
stack_nodes.push(node.left)
assert stack_nodes.is_empty()
return root_node
I would highly appreciate if you could review the code and suggest any approaches for improvement. For example, I am trying to understand if it is possible to use only one stack.
EDIT After looking into my code in the morning, I realise that there is no need for two stacks because I always push and pop to the two stacks at the same time. Here there is an updated version of the code:
def build_bst_iterative_one_stack(array):
size = len(array)
# Add to the stack a node that will be a root of the tree:
root_node = Node(None)
# Stack stores tuples of the current node, and the first and the last indices of half-segments:
stack = Stack()
stack.push((root_node, 0, size - 1))
while not stack.is_empty():
node, first, last = stack.pop()
if last < first:
# The segment is degenerated. Do nothing.
continue
elif last == first:
# Assign the value, it is the last bottom node.
node.value = array[last]
continue
mid = (first + last) // 2
node.value = array[mid]
node.left = Node(None)
node.right = Node(None)
# Update the stack with the left and the right half-segment:
stack.push((node.right, mid + 1, last))
stack.push((node.left, first, mid - 1))
assert stack.is_empty()
return root_node
list.append/pop? The sole reason I tried to understand that non-commented code was that I was curious whether you presented a bottom-up procedure.) \$\endgroup\$list. I also added some comments to make the iterative algorithm easier to understand. In a bottom-up procedure you would build tree from bottom? In this case I have a top-down procedure. I would highly appreciate if you could elaborate how to implement a bottom-up procedure. Also, what is also the first thing striking about the iterative approach? \$\endgroup\$