I developed a heap sort variant that sorts a heap through traversal. Unlike in the standard heap sort, the algorithm does not remove the min element from the heap instead it traverses all the nodes of the heap visiting those with smaller values first and adding them to the sorted list
Here’s the implementation of the algorithm in python:
import heapq
def heap_sort(lst):
"""
Performs a heap sort iteratively by traversing all nodes of the heap, visiting nodes with smaller values first and adding the values to sorted result list.
Args:
lst (list): The input list to be sorted.
Returns:
list: A sorted list containing elements of the input list in ascending order.
Approach:
- Convert the input list into a min-heap
- Traverse and process the heap iteratively:
- Maintain a set to track indices that have already been processed.
- Replace the value at the current node with the value of either the left child, right child or parent, depending on specific conditions.
- Accumulate results in a separate list as each node is processed.
"""
def left_child(i):
"""
Returns the value of the left child of the node at index i, or infinity if out of bounds.
"""
return float("inf") if 2 * i + 1 >= len(lst) else lst[2 * i + 1]
def right_child(i):
"""
Returns the value of the right child of the node at index i, or infinity if out of bounds.
"""
return float("inf") if 2 * i + 2 >= len(lst) else lst[2 * i + 2]
def parent(i):
"""
Returns the value of parent of the node at index i, or infinity if the node is the root.
"""
return lst[(i - 1) // 2] if i > 0 else float("inf")
heapq.heapify(lst) # Build a min-heap from input list
# A set to keep track of visited indices (nodes)
visited_indices = set()
# List to store the sorted result
result = []
# Start traversal from the root of the heap
current_index = 0
while len(result) < len(lst):
if current_index not in visited_indices:
# Add the current node's value to the result and mark it as visited
result.append(lst[current_index])
visited_indices.add(current_index)
# Replace the current node value with value of either left, right or parent node
if parent(current_index) < min(left_child(current_index), right_child(current_index)):
lst[current_index] = min(left_child(current_index), right_child(current_index))
current_index = (current_index - 1) // 2 # Move to the parent node
elif left_child(current_index) < right_child(current_index):
lst[current_index] = min(right_child(current_index), parent(current_index))
current_index = 2 * current_index + 1 # Move to the left child
else:
lst[current_index] = min(left_child(current_index), parent(current_index))
current_index = 2 * current_index + 2 # Move to the right child
return result
The algorithm's main steps are:
Convert the input list into a min-heap using
heapq.heapify.Initialize result to an empty list [ ]
Traverse the heap iteratively starting with root node and replace the current node's value with either the value of parent, left child or right child based on specific conditions. The traversal logic ensures that the parent node "calls" the smaller of it's children to explore its subtree until a node with value greater than the parent is encountered. If the children of the current node are all greater than the value of the parent of current node, it means the parent node has nodes in its other subtree that have smaller values. In this case, control is returned to parent node
Append the node’s value to the result when it is first visited.
My Questions
- How does the algorithm compare to the standard heap sort in terms of efficiency?
- Can it be optimized?
- Could the iterative traversal logic be simplified while maintaining correctness?
For those interested in a recursive solution, here's the implementation:
import heapq
def heap_sort_re(lst):
def left_child(i):
return float("inf") if 2 * i + 1 >= len(lst) else lst[2 * i + 1]
def right_child(i):
return float("inf") if 2 * i + 2 >= len(lst) else lst[2 * i + 2]
def parent(i):
return lst[(i - 1) // 2] if i > 0 else float("inf")
def _recurse(i = 0):
if len(result) == len(lst):
return
if i not in visited:
result.append(lst[i])
visited.add(i)
if parent(i) < min(left_child(i), right_child(i)):
lst[i] = min(left_child(i), right_child(i))
_recurse((i - 1) // 2)
elif left_child(i) < right_child(i):
lst[i] = min(right_child(i), parent(i))
_recurse( 2*i + 1)
else:
lst[i] = min(left_child(i), parent(i))
_recurse(2*i + 2)
heapq.heapify(lst)
visited = set()
result = []
_recurse()
return result



sorted(lst), I am not yet seeing it. Maybe you wanted to add a "reinventing the wheel" tag, to show that this is strictly a learning exercise, not destined for production code? \$\endgroup\$nsmallest(len(lst), lst). \$\endgroup\$