Skip to content
Advertisement

Non-recursive Quicksort

How do i make the bottom function non-recursive, ive tried but by creating new functions which is not the point in this problem. The first function is given and the inplace_quicksort_non_recursive is created by me.

import random

def inplace_quick_sort(S, a, b):
  """Sort the list from S[a] to S[b] inclusive using the quick-sort algorithm."""
  if a >= b: return                                      # range is trivially sorted
  pivot = S[b]                                           # last element of range is pivot
  left = a                                               # will scan rightward
  right = b-1                                            # will scan leftward
  while left <= right:
    # scan until reaching value equal or larger than pivot (or right marker)
    while left <= right and S[left] < pivot:
      left += 1
    # scan until reaching value equal or smaller than pivot (or left marker)
    while left <= right and pivot < S[right]:
      right -= 1
    if left <= right:                                    # scans did not strictly cross
      S[left], S[right] = S[right], S[left]              # swap values
      left, right = left + 1, right - 1                  # shrink range

  # put pivot into its final place (currently marked by left index)
  S[left], S[b] = S[b], S[left]
  # make recursive calls
  inplace_quick_sort(S, a, left - 1)
  inplace_quick_sort(S, left + 1, b)
  return left
  

def inplace_quick_sort_nonrecursive(S):
    stack = []                # create a stack for storing sublist start and end index
    a = 0                             # get the starting and ending index of a given list
    b = len(S) - 1
    pivot = S[b]
 
    stack.append((a, b))              # push the start and end index of the array into the stack

    while len(stack) > 0:                      # loop till stack is empty
      a, b = stack.pop()              # remove top pair from the list and get sublist starting and ending indices
      pivot = inplace_quick_sort(S, a, b)           # rearrange elements across pivot
      if pivot - 1 > a:               # push sublist indices containing elements that are less than the current pivot to stack
        stack.append((a, pivot - 1))
      if pivot + 1 < b:               # push sublist indices containing elements that are more than the current pivot to stack
        stack.append((pivot + 1, b))


origList = random.sample(range(100), 100)
origList2 = random.sample(range(100), 100)
origList.extend(origList2)
inplace_quick_sort_nonrecursive(origList)

errorIndices = []
for i in range(100):
  ind1 = 2*i
  ind2 = ind1+1
  if origList[ind1] != i:
    errorIndices.append(ind1)
  if origList[ind2] != i:
    errorIndices.append(ind2)
if len(errorIndices) == 0:
  print("PASSED")
else:
  print("Error in indices: " + str(errorIndices))

What do i need to create so the bottom function becomes non-recursive

Advertisement

Answer

The question is using a variation of Hoare partition scheme (but with issues). Example with classic Hoare partition scheme. Note Hoare splits partition into elements <= pivot and elements >= pivot; the pivot and elements == pivot can end up anywhere, so Hoare only splits partition into 2 parts (the pivot is not put into place and cannot be excluded from later partition steps).

import random
from time import time

def qsort(a):
    if len(a) < 2:                      # if nothing to sort, return
        return
    stack = []                          # initialize stack
    stack.append([0, len(a)-1])
    while len(stack) > 0:               # loop till stack empty
        lo, hi = stack.pop()            # pop lo, hi indexes
        p = a[(lo + hi) // 2]           # pivot, any a[] except a[hi]
        i = lo - 1                      # Hoare partition
        j = hi + 1
        while(1):
            while(1):                   #  while(a[++i] < p)
                i += 1
                if(a[i] >= p):
                    break
            while(1):                   #  while(a[--j] < p)
                j -= 1
                if(a[j] <= p):
                    break
            if(i >= j):                 #  if indexes met or crossed, break
                break
            a[i],a[j] = a[j],a[i]       #  else swap elements
        if(j > lo):                     # push indexes onto stack
            stack.append([lo, j])
        j += 1
        if(hi > j):
            stack.append([j, hi])

# test sort
a = [random.randint(0, 2147483647) for r in range(512*1024)]
s = time()
qsort(a)
e = time()
print e - s

# check to see if data was sorted
f = 0
for i in range (1 ,len(a)):
    if(a[i-1] > a[i]):
        f = 1
        break
if(f == 0):
    print("sorted")
else:
    print("error")

In response to comment, a simple example in C++.

void QuickSort(uint64_t arr[], size_t lo, size_t hi)
{
uint64_t * stk = new size_t [hi+1-lo];  // allocate "stack"
size_t stki = 0;                        // stack index

    stk[stki+1] = hi;                   // init stack
    stk[stki+0] = lo;
    stki += 2;
    while(stki != 0){
        stki -= 2;
        lo = stk[stki+0];
        hi = stk[stki+1];
        if(lo >= hi)
            continue;
        uint64_t pivot = arr[lo + (hi - lo) / 2];
        size_t i = lo - 1;
        size_t j = hi + 1;
        while (1)
        {
            while (arr[++i] < pivot);
            while (arr[--j] > pivot);
            if (i >= j)
                break;
            std::swap(arr[i], arr[j]);
        }
        stk[stki+3] = hi;
        stk[stki+2] = j+1;
        stk[stki+1] = j;
        stk[stki+0] = lo;
        stki += 4;
    }
    delete [] stk;              // free "stack"
}

The original version of quicksort limited stack space to 2*ceil(log2(size)) by pushing indexes of larger partition onto stack, and looping on smaller partition. Hoare used the term “nest” instead of stack in his original paper.

void QuickSort(uint64_t arr[], size_t lo, size_t hi)
{
    if(lo >= hi)return;                 // if less than 2 elements, return
                                        // allocate stack
    size_t s = 2*(size_t)(ceil(log2(hi+1-lo)));
    uint64_t * stk = new size_t [s];
    s = 0;                              // stack index
    stk[s++] = hi;                      // init stack
    stk[s++] = lo;
    while(s != 0){                      // while more partitions
        lo = stk[--s];
        hi = stk[--s];
        while(lo < hi){                 // while partion size > 1
            uint64_t pivot = arr[lo+(hi-lo)/2];
            size_t i = lo-1;            // Hoare parttion
            size_t j = hi+1;
            while (1)
            {
                while (arr[++i] < pivot);
                while (arr[--j] > pivot);
                if (i >= j)
                    break;
                std::swap(arr[i], arr[j]);
            }
            i = j++;                    // i = last left, j = first right
            if(i - lo > hi - j){        // push larger partition indexes to stack,
                if (lo < i) {           //  loop on smaller
                    stk[s++] = i;
                    stk[s++] = lo;
                }
                lo = j;
                continue;
            } else {
                if (j < hi) {
                    stk[s++] = hi;
                    stk[s++] = j;
                }
                hi = i;
                continue;
            }
        }
    }
    delete [] stk;                      // free "stack"
}
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement