There are non recursive versions of heapify (see example below). For quicksort, if recursion is only used on the smaller partition, then looping back to split what was the larger partition into 2 (again using recursion on the smaller of those 2 partitions and so on), then max stack space is O(log(n)), but worst case time is still O(n^2).
C++ example of non recursive heap sort with non recursive heapify:
typedef unsigned int uint32_t;
void HeapSort(uint32_t *, size_t);
void Heapify(uint32_t *, size_t);
void SiftDown(uint32_t *, size_t, size_t);
void HeapSort(uint32_t * a, size_t count)
{
size_t end;
Heapify(a, count); // create initial heap
end = count-1;
while(end > 0){
// swap root (largest value) with end
std::swap(a[0], a[end]);
// reduce size of heap and
// increase size of sorted array
end--;
// repair the reduced heap
SiftDown(a, 0, end);
}
}
// create initial heap: for root = (count-2)/2 -> 0
// parent = root, children = root*2+1, root*2+2
// swap so that all a[parent] > a[child]
void Heapify(uint32_t * a, size_t count)
{
size_t root;
if(count < 2)
return;
// create sub-heaps of increasing size,
// with root going from (count-2)/2 to 0
root = (count - 2) / 2;
while(1){
SiftDown(a, root, count-1);
if(root == 0)
break;
root--;
}
}
// scan from root to end, swapping as needed to repair or create heap
void SiftDown(uint32_t * a, size_t root, size_t end){
size_t parent;
size_t child;
// while at least two children
for(parent = root; (child = parent * 2 + 2) <= end; ){
// choose the larger child
if(a[child-1] > a[child])
child = child-1;
// if child > parent then swap, parent = child
if(a[child] > a[parent]){
std::swap(a[child], a[parent]);
parent = child;
// else done with search for swaps
} else {
break;
}
}
// check for final only child
if((child = parent * 2 + 1) <= end)
if(a[child] > a[parent])
std::swap(a[child], a[parent]);
}