To understand the value of recursion in a programming langua
To understand the value of recursion in a programming language, write a program that implements quicksort, first using recursion and then without. Use the web to look up what the particulars of the Quicksort program are. Please explain.
Solution
Hi, Please find my explanation and implementation
Please go through all comments to understand about recursive and iterative implementation.
Please let me know in case of any issue.
######## Recursive #############
QuickSort is a Divide and Conquer algorithm. It picks an element as pivot and partitions the given array around the picked pivot. There are many different versions of quickSort that pick pivot in different ways.
Always pick first element as pivot.
Always pick last element as pivot (implemented below)
Pick a random element as pivot.
Pick median as pivot.
The key process in quickSort is partition(). Target of partitions is, given an array and an element x of array as pivot, put x at its correct position in sorted array and put all smaller elements (smaller than x) before x, and put all greater elements (greater than x) after x. All this should be done in linear time.
// Java program for implementation of QuickSort
class QuickSort
{
/* This function takes last element as pivot,
places the pivot element at its correct
position in sorted array, and places all
smaller (smaller than pivot) to left of
pivot and all greater elements to right
of pivot */
int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int i = (low-1); // index of smaller element
for (int j=low; j<=high-1; j++)
{
// If current element is smaller than or
// equal to pivot
if (arr[j] <= pivot)
{
i++;
// swap arr[i] and arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// swap arr[i+1] and arr[high] (or pivot)
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i+1;
}
/* The main function that implements QuickSort()
arr[] --> Array to be sorted,
low --> Starting index,
high --> Ending index */
void sort(int arr[], int low, int high)
{
if (low < high)
{
/* pi is partitioning index, arr[pi] is
now at right place */
int pi = partition(arr, low, high);
// Recursively sort elements before
// partition and after partition
sort(arr, low, pi-1);
sort(arr, pi+1, high);
}
}
/* A utility function to print array of size n */
static void printArray(int arr[])
{
int n = arr.length;
for (int i=0; i<n; ++i)
System.out.print(arr[i]+\" \");
System.out.println();
}
// Driver program
public static void main(String args[])
{
int arr[] = {10, 7, 8, 9, 1, 5};
int n = arr.length;
QuickSort ob = new QuickSort();
ob.sort(arr, 0, n-1);
System.out.println(\"sorted array\");
printArray(arr);
}
}
############ Iterative ##########################
1) The above implementation uses last index as pivot. This causes worst-case behavior on already sorted arrays, which is a commonly occurring case. The problem can be solved by choosing either a random index for the pivot, or choosing the middle index of the partition or choosing the median of the first, middle and last element of the partition for the pivot.
2) To reduce the recursion depth, recur first for the smaller half of the array, and use a tail call to recurse into the other.
3) Insertion sort works better for small subarrays. Insertion sort can be used for invocations on such small arrays (i.e. where the length is less than a threshold t determined experimentally). For example, this library implementation of qsort uses insertion sort below size 7.
Despite above optimizations, the function remains recursive and uses function call stack to store intermediate values of l and h. The function call stack stores other bookkeeping information together with parameters. Also, function calls involve overheads like storing activation record of the caller function and then resuming execution.
The above function can be easily converted to iterative version with the help of an auxiliary stack. Following is an iterative implementation of the above recursive code.
// Java implementation of iterative quick sort
class IterativeQuickSort
{
void swap(int arr[],int i,int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
/* This function is same in both iterative and
recursive*/
int partition (int arr[], int l, int h)
{
int x = arr[h];
int i = (l - 1);
for (int j = l; j <= h- 1; j++)
{
if (arr[j] <= x)
{
i++;
// swap arr[i] and arr[j]
swap(arr,i,j);
}
}
// swap arr[i+1] and arr[h]
swap(arr,i+1,h);
return (i + 1);
}
// Sorts arr[l..h] using iterative QuickSort
void QuickSort(int arr[], int l, int h)
{
// create auxiliary stack
int stack[] = new int[h-l+1];
// initialize top of stack
int top = -1;
// push initial values in the stack
stack[++top] = l;
stack[++top] = h;
// keep popping elements until stack is not empty
while (top >= 0)
{
// pop h and l
h = stack[top--];
l = stack[top--];
// set pivot element at it\'s proper position
int p = partition(arr, l, h);
// If there are elements on left side of pivot,
// then push left side to stack
if ( p-1 > l )
{
stack[ ++top ] = l;
stack[ ++top ] = p - 1;
}
// If there are elements on right side of pivot,
// then push right side to stack
if ( p+1 < h )
{
stack[ ++top ] = p + 1;
stack[ ++top ] = h;
}
}
}
// A utility function to print contents of arr
void printArr( int arr[], int n )
{
int i;
for ( i = 0; i < n; ++i )
System.out.print(arr[i]+\" \");
}
// Driver code to test above
public static void main(String args[])
{
IterativeQuickSort ob = new IterativeQuickSort();
int arr[] = {4, 3, 5, 2, 1, 3, 2, 3};
ob.QuickSort(arr, 0, arr.length-1);
ob.printArr(arr, arr.length);
}
}
The above mentioned optimizations for recursive quick sort can also be applied to iterative version.
1) Partition process is same in both recursive and iterative. The same techniques to choose optimal pivot can also be applied to iterative version.
2) To reduce the stack size, first push the indexes of smaller half.
3) Use insertion sort when the size reduces below a experimentally calculated threshold.



