trying to implement Quicksort Algorithm - java

Just started learning algorithms: trying to implement the quicksort algorithm with Java. But it's not showing anything in the output tried many times but unable to find the reason.
It is not showing anything.
public class Try {
public static void main(String[] args) {
int []arr= {22,9,8,45,28,7,1};
int len = arr.length;
quicksort(arr, 0, len-1);
for(int i = 0; i<len; i++)
System.out.print(arr[i]+" ");
}
static void quicksort(int [] arr, int low, int high) {
if (low < high) {
int index = partition(arr, low, high);
quicksort(arr, low, index -1);
quicksort(arr, index+1, high);
}
}
static int partition(int [] arr, int low, int high) {
int pivot = arr[low];
int i = low;
int j = high;
while(i<=j) {
while(arr[i]<pivot) i++;
while(arr[j]>pivot) j--;
if(i<=j) {
// swapping i with j
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//swapping pivot(low) with j when i<j
int temp = arr[low];
arr[low] = arr[j];
arr[j] = temp;
}
return j;
}
}

Your problem comes from the partition method. You keep getting stuck in the second while loop. You should do it with a single loop, here's an exemple ( I personally prefer using a for loop ) :
int pivot = arr[high];
int i = (low - 1); // Index of smaller element and indicates the
// right position of pivot found so far
for (int j = low; j <= high- 1; j++)
{
// If current element is smaller than the pivot
if (arr[j] < pivot)
{
i++; // increment index of smaller element
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);

Related

What is wrong with this code when i sort only a portion of an array not starting from index 0?

When I use mergeSort to sort a portion of an array it gives ArrayIndexOutOfBoundsException but if I merge a portion starting from the index 0 to any other index it works why?
public static void mergeSort(int[] arr, int[] temp, int low, int high) {
if (low < high) {
int mid = low + (high - low) / 2;
mergeSort(arr, temp, low, mid);
mergeSort(arr, temp, mid + 1, high);
merge(arr, temp, low, mid, high);
}
}
public static void merge(int[] arr, int[] temp, int low, int mid, int high) {
for (int i = low; i <= high; i++) {
temp[i] = arr[i];
}
int i = low;
int j = mid + 1;
int k = low;
while (i <= mid && j <= high) {
if (temp[i] <= temp[j]) {
arr[k] = temp[i];
i++;
} else {
arr[k] = temp[j];
j++;
}
k++;
}
while (i <= mid) {
arr[k] = temp[i];
i++;
k++;
}
}
I get an error ArrayIndexOutOfBoundsException.
The argument high in your code is the index of the last element in the slice to be sorted or merged. Are you sure you pass array.length - 1 as the second argument to the topmost call to mergeSort()?
Also make sure you allocate the temp array with the same length as the array to be sorted.
This convention is confusing and does not allow sorting empty arrays. You might instead use a more consistent convention where high is the index of the element after the last one and you pass array.length to the topmost call.
Here is a modified version:
public static void mergeSort(int[] arr, int[] temp, int low, int high) {
if (high - low > 1) {
int mid = low + (high - low) / 2;
mergeSort(arr, temp, low, mid);
mergeSort(arr, temp, mid, high);
merge(arr, temp, low, mid, high);
}
}
public static void merge(int[] arr, int[] temp, int low, int mid, int high) {
for (int i = low; i < high; i++) {
temp[i] = arr[i];
}
int i = low;
int j = mid;
int k = low;
while (i < mid && j < high) {
if (temp[i] <= temp[j]) {
arr[k++] = temp[i++];
} else {
arr[k++] = temp[j++];
}
}
while (i < mid) {
arr[k++] = temp[i++];
}
}

I am having trouble with using Median and Mean as pivot in Quicksort

For a class project I am supposed to test Quicksort with different pivots (Low, High, Midpoint, Random, Median, and Mean), but I am having trouble getting it to work with wall of them. So far I have used two different Quicksort methods and I have been able to sort a random array with everything up to random, but not with mean or median. "Partition" work with Low, Midpoint and Random, while "Partition1" works with High.
Any help is greatly appreciated and If there is any more I should add just let me know.
Here is the code that I've used so far. "Partition1" is from GFG and "Partition" is from my textbook.
`
import java.util.Arrays;
import java.util.Random;
public class QSort {
public static int comparisonCount;
public static int swapCount;
public static void main(String[] args){
Random r = new Random();
int N= 10;
int[] test = new int[N]; //random integer array
for (int i =0; i < test.length; i++){
test[i] = r.nextInt(N*2);
}
System.out.println(Arrays.toString(test));
long startTime = System.nanoTime();
Quicksort(test, 0, test.length-1);
long endTime = System.nanoTime();
long duration = (endTime - startTime);
System.out.println(Arrays.toString(test));
System.out.println("Number of Comparisons "+ comparisonCount);
System.out.println("Number of swaps "+ swapCount);//1000000
System.out.println("QuickSort Duration in millisecond is "+(duration/1000000));
System.out.println("length is "+(test.length-1));
}
public static double median(int[] arr){
double median;
if (arr.length % 2 == 0) {
median = ((double) arr[arr.length / 2] + (double) arr[arr.length / 2 - 1]) / 2;
}
else {
median = (double) arr[arr.length / 2];
}
return median;
}
static void swap(int[] arr, int i, int j)
{
swapCount++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static int Partition1(int[] arr, int low, int high)
{
int pivot =arr[high];
int i = (low - 1);
for(int j = low; j <= high - 1; j++)
{
if(largerThan(arr[j],pivot))
{
i++;
swap(arr, i, j);
}
}
swap(arr, i+1, high);
return (i+1);
}
public static int mean(int[] arr){
double total = 0;
for(int i=0; i<arr.length; i++){
total = total + arr[i];
}
double average = total / arr.length;
return (int)average;
}
public static int Partition(int[] numbers, int low, int high) {
int midpoint = low + (high - low)/2; // Calculate Midpoint
//Random rand= new Random(); // for random pivot
//int pivot = numbers[rand.nextInt(high-low)+low];
int pivot = numbers[high];
boolean done = false;
while (!done) {
while (largerThan(numbers[low], pivot)) {
low+=1;
}
while (largerThan(pivot, numbers[high])) {
high-=1;
}
if (low >= high) {
done = true;
}
else {
swap(numbers, low, high);
low+=1;
high-=1;
}
}
return high;
}
public static boolean largerThan( int i, int m){
comparisonCount++;
return i < m;
}
public static void Quicksort(int[] numbers, int low, int high) {
if (high <= low || low >= high) {
return;
}
var Index = Partition1(numbers, low, high);
Quicksort(numbers, low, Index-1); //Index-1 for Partion1 and just Index for Partition
Quicksort(numbers, Index + 1, high);
}
}
`
Example C code for Lomuto Partition. It swaps middle element to last, but that can be removed. Recurse on smaller, loop on larger limits stack space complexity to O(log2(n)), but worst case time complexity remains O(n^2). You don't need uint64_t either (just use int instead).
void QuickSort(uint64_t a[], int lo, int hi)
{
while (lo < hi){
uint64_t t;
uint64_t p = a[(lo+hi)/2]; /* use mid point for pivot */
a[(lo+hi)/2]= a[hi]; /* swap with a[hi] */
a[hi] = p;
int i = lo;
for (int j = lo; j < hi; ++j){ /* Lomuto partition */
if (a[j] < p){
t = a[i];
a[i] = a[j];
a[j] = t;
++i;
}
}
t = a[i];
a[i] = a[hi];
a[hi] = t;
if(i - lo <= hi - i){ /* recurse on smaller partiton, loop on larger */
QuickSort(a, lo, i-1);
lo = i+1;
} else {
QuickSort(a, i+1, hi);
hi = i-1;
}
}
}

How do I fix my quicksort algorithm for larger values of n ? ( and when the array is not random)

My quicksort algorithm fails for larger values of n, and only if the array is not random. I've tried using the algorithm on various arrays. It works fine when I use a random array of numbers (for any value of n), but for an array that contains the same values or values in ascending order or descending order, It fails. And that too only when n is approximately abov 6000. ( It works perfectly when n is <5000)
I've already tried using a different version of quicksort. One that uses a while loop instead of recursion, and it works perfectly. And like I've said already my algorithm fails only when n is greater than 6000 for a nonrandomized array, for 5000 or below it works well.
void quicksort(int a[], int low, int high) {
if (low < high) {
int index = partition(a, low, high); // error
quicksort(a, low, index - 1); // error
quicksort(a, index + 1, high);
}
}
int partition(int arr[], int low, int high) {
int pivot = arr[high];
int i = low - 1;
//int j = low;
for (int j = low; j < high; 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;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1);
}
Above I have my quicksort algorithm. The one that fails for n>6000( and when the array is not random).
And below is the code that worked for all values of n and on any type of array.
public void quicksort(int[] data, int low, int high)
{ // 1 or 0 items are sorted by default
if(high - low < 1)
return;
int left = low;
int right = high;
int pivot = data[low + (high - low) / 2];
while(left <= right)
{ // Increment left pointer until left >= pivot
while(data[left] < pivot)
left++;
// Increment right pointer until right <= pivot
while(data[right] > pivot)
right--;
// If left < right; swap values
if(left <= right)
{ int temp = data[left];
data[left] = data[right];
data[right] = temp;
left++;
right--;
}
}
// quick_sort 'lesser values'
quicksort(data, low, right);
// quick_sort 'greater values'
quicksort(data, left, high);
}
static int partition(int[] array, int low, int high) {
int j, temp, i = low + 1;
Random random = new Random();
int x = random.nextInt(high - low) + low;
temp = array[low];
array[low] = array[x];
array[x] = temp;
for (j = low + 1; j <= high; j++) {
if (array[j] <= array[low] && j != i) {
temp = array[j];
array[j] = array[i];
array[i++] = temp;
} else if (array[j] <= array[low]) {
i++;
}
}
temp = array[i - 1];
array[i - 1] = array[low];
array[low] = temp;
return i - 1;
}
The terminal shows an error in two lines specifically. (The lines that I have marked as an error in the first quicksort method).
If the data is already in order, then using arr[high] (or arr[low]) results in worst case stack space overhead of O(n), which overflows the stack. The second example, uses the middle element (arr[low + (high-low)/2]), which will have best case stack space overhead for data already sorted or data already reverse sorted.
A workaround to limit stack space overhead to O(log(n)), is after doing partition, check to see which part is smaller, and only use recursion on the smaller part, then loop back to handle the larger part (update low or high as needed to exclude the now sorted smaller part before looping back).
public static void quicksort(int[] arr, int low, int high)
{
while (low < high) {
int index = partition(arr, low, high);
if((index-low) <= (high-index)){ // avoid stack overflow
quicksort(arr, low, index - 1); //
low = index+1; //
}else{ //
quicksort(arr, index + 1, high); //
high = index-1; //
} //
}
}
public static int partition(int[] arr, int low, int high)
{
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (arr[j] <= pivot) {
i++;
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
int tmp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = tmp;
return (i + 1);
}
If interested, Hoare partition scheme is faster:
public static void qsort(int[] a, int lo, int hi)
{
while(lo < hi){
int md = lo+(hi-lo)/2;
int ll = lo-1;
int hh = hi+1;
int p = a[md];
int t;
while(true){
while(a[++ll] < p);
while(a[--hh] > p);
if(ll >= hh)
break;
t = a[ll];
a[ll] = a[hh];
a[hh] = t;
}
ll = hh++;
if((ll - lo) <= (hi - hh)){
qsort(a, lo, ll);
lo = hh;
} else {
qsort(a, hh, hi);
hi = ll;
}
}
}

Counting the comparisons and swaps in a quick sort

I am running a quick sort on 2000 integers read from a file but the number of comparisons and swaps i am getting seem high. Are my counters in the right place? or is something wrong with the sort?
public int partition(int array[], int low, int high)
{
int pivot = array[high];
int i = (low-1);
for (int j = low; j < high; j++)
{
compCounter++;
if (array[j] <= pivot)
{
i++;
int temp = array[i];
array[i] = array[j];
array[j] = temp;
SwapCounter++;
}
}
int temp = array[i+1];
array[i+1] = array[high];
array[high] = temp;
SwapCounter++;
return i+1;
}
public void quickSort(int array[], int low, int high)
{
if (low < high)
{
int pivotPoint = partition(array, low, high);
quickSort(array, low, pivotPoint-1);
quickSort(array, pivotPoint+1, high);
}
}
Your counters are correct.
Just a quick suggestion - move the swap code in a separate function swap(array, fromIndex, toIndex)

Count the number of checks each method had to perform before fully sorting the array

So I'm writing a program that compares Bubble, Selection, Merge, and Quick Sort. All 4 methods are given a randomized array of 1000 elements and I count to see how many times it takes a method to perform to fully sort the array. My question revolves around the placement of my counts. I know I should look at the Big O complexities for each to get a ballpark estimate of the number but I'm just asking if I put the counts in the right spots.
Bubble Sort:
public static int[] bubbleSort(int[] a)
{
boolean done = false;
int n = a.length;
while(done == false)//runs n times
{
done = true;
for(int i = 0; i < n-1; i++)//runs n times
{
if(a[i] > a[i+1])//Swap
{
bubbleCount++;
int temp = a[i];
a[i] = a[i+1];
a[i+1] = temp;
done = false;
}
}
}
return a;
}
Selection Sort:
public static void selectionSort(int[] a )
{
for (int i = 0; i < a.length - 1; i++)
{
int pos = i;
for (int j = i + 1; j < a.length; j++)
{
// increments selection count
selectionCount++;
// if a[j] is less than a[pos], set pos to j
if (a[j] < a[pos])
{
pos = j;
}
}
// swaps a[i] and a[pos]
int temp = a[i];
a[i] = a[pos];
a[pos] = temp;
}
}
Merge Sort:
public static void mergeSort(int [] a)
{
//put counter for checks inside method
int size = a.length;
if(size < 2)//Halt recursion
{
return;
}
int mid = size/ 2;
int leftSize = mid;
int rightSize = size - mid;
int[] left = new int[leftSize];
int[] right = new int[rightSize];
//populate left
for(int i = 0; i < mid; i++)
{
mergeCount++;
left[i] = a[i];
}
//populate right
for(int i = mid; i < size; i++)
{
mergeCount++;
right[i-mid] = a[i];
}
mergeSort(left);
mergeSort(right);
//merge
merge(left, right, a);
}
public static void merge(int[] left, int[] right, int[] a)
{
int leftSize = left.length;
int rightSize = right.length;
int i = 0;//index for left
int j = 0;//index for right
int k = 0;//index for a
while(i < leftSize && j < rightSize)//compares until the end is reach in either
{
if(left[i] <= right[j])
{
//assigns a[k] to left[i] and increments both i and k
a[k] = left[i];
i++;
k++;
}
else
{
//assigns a[k] to right [j] and increments j and k
a[k] = right[j];
j++;
k++;
}
}
//fills in the rest
while(i<leftSize)
{
a[k] = left[i];
i++;
k++;
}
while(j<rightSize)
{
a[k] = right[j];
j++;
k++;
}
}
Quick Sort
public static void quickSort(int[] a, int left, int right)
{
int index = partition(a, left, right);
if(left < index - 1)
{
//increments quickCount and calls quickSort recursively
quickCount++;
quickSort(a, left, index-1);
}
if(index < right)
{
//increments quickCount and calls quicSort recursively
quickCount++;
quickSort(a, index, right);
}
}
public static int partition(int[] a, int left, int right)
{
int i = left;
int j = right;
int pivot = a[((left+right)/2)];
while(i<=j)
{
while(a[i] < pivot)
{
i++;//correct position so move forward
}
while(a[j] > pivot)
{
j--;//correct position
}
if(i <= j)
{
//swaps and increments i and decrements j
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i++;
j--;
}
}
return i;
}
Your counts should be wherever you do a comparison of two values.
The bubbleCount should be moved up above the if because you want to count every comparison not sure the ones where the if clause is true. The selectionCount seems okay. The mergeCount should be moved to where you compare values.
You need to count comparisons, so I would create a method to compare and count:
static int count;
private static boolean isGreaterThan(int i, int j) {
count++;
return i > j;
}
and always use that method to compare values:
instead of:
while(i<=j)
do:
while(!isGreaterThan(i, j))
etc

Categories

Resources