Quicksort doesn't run Java [duplicate] - java

This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 4 years ago.
Im trying to make a Quicksort but it always showing up the Error ArrayIndexOutOfBoundsException.
public class Quicksort
{
void sort(int[] arr)
{
_quicksort(arr, 0, arr.length - 1);
}
private void _quicksort(int[] arr, int left, int right)
{
int pivot = (left + right)/2;
int l = left;
int r = right;
while (l <= r)
{
while (arr[l] < pivot)
{
l++;
}
while (arr[r] > pivot)
{
r--;
}
if(l <= r)
{
int temp = l;
l = r;
r = temp;
l++;
r++;
}
}
if(left < r)
{
_quicksort(arr, left, r);
}
if(l < right)
{
_quicksort(arr, l, right);
}
}
}
Does someone know why it doesnt run? It always gives a Error.
The Error message is
java.lang.ArrayIndexOutOfBoundsException: -1
at Quicksort._quicksort(Quicksort.java:18)
at Quicksort._quicksort(Quicksort.java:33)
at Quicksort.sort(Quicksort.java:5)
Error Message

It seems like there are a couple of issues with your code. I've listed them below:
The variable pivot stores the index of the pivot element and not the actual value. So, you can't use pivot for comaparison as you have done in the 2 nested while loops. You need arr[pivot] instead of pivot there.
Imagine arr looks like {1, 1, 1, 3, 2, 2, 2}. Here, pivot will be equal to 3 i.e. arr[pivot] will be equal to 3. Now, after both the nested while loops terminate, l will be equal to 3 and r will remain equal to 6. You then swap arr[l] and arr[r] and increment both l and r. Since l is still less than equal to r, the outer while loop runs for a second time and you'll get an ArrayIndexOutOfBoundsExecption when the control reaches the second nested while loop. This happens because you're trying to access arr[7] (Out of Bounds).
Here's my code:
class Quicksort
{
void sort(int[] arr)
{
myQuicksort(arr, 0, arr.length - 1);
}
private void myQuicksort(int[] arr, int l, int r) {
if (l >= r) {
return;
}
int pivotIndex = (l + r) / 2;
swap (arr, r, pivotIndex);
int pivotValue = arr[r];
int swapIndex = 0;
int currentIndex = 0;
while (currentIndex != r) {
if (arr[currentIndex] < pivotValue) {
swap(arr, currentIndex, swapIndex);
swapIndex++;
}
currentIndex++;
}
swap(arr, r, swapIndex);
myQuicksort(arr, l, swapIndex - 1);
myQuicksort(arr, swapIndex + 1, r);
}
private void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
public class Main{
public static void main(String[] args) {
Quicksort quicksort = new Quicksort();
int[] arr = {3, 7, 1, 0, 4};
quicksort.sort(arr);
for (int i : arr) {
System.out.println(i);
}
}
}
You should read up on Quicksort. But here are the main points:
Choose a random pivot element and swap it with the last element. This makes the implementation much simpler.
Loop over the input array and keep a track of a swapIndex such that everything before the swapIndex is less than the pivotValue and everything from the swapIndex till the currentIndex is greater than (or equal) the pivotValue.
After the loop runs out, swap the element at swapIndex with the pivot. This inserts the pivot in its correct position.
The pivot divides the array into 2 subarrays. Call myQuicksort on these 2 subarrays.

Related

Implementing Merge Sort with Java

I was trying to implement merge sort using java, but it's saying:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at HelloWorld.merge(HelloWorld.java:42)
at HelloWorld.sort(HelloWorld.java:30)
at HelloWorld.sort(HelloWorld.java:28)
at HelloWorld.sort(HelloWorld.java:29)
at HelloWorld.sort(HelloWorld.java:28)
at HelloWorld.main(HelloWorld.java:10)
So I tried to copy the original array into a helper array in the merge subroutine, I think it's the length I use for helper array messed everything up, isn't "r-l+1" the length of the helper array?
If I put 100 as the length of the helper array, the code would work, but obviously it's not going to work when the size of the original array gets larger.
Please help and thanks in advance.
public class HelloWorld{
public static void main(String []args){
HelloWorld ms = new HelloWorld();
int arr[] = new int[]{4,3,0,1,3,2,4,20,13,22,10};
ms.sort(arr, 0, arr.length - 1);
ms.printArr(arr);
}
void printArr(int arr[])
{
int n = arr.length;
for(int i = 0; i<n; i++){
System.out.print(" "+arr[i]);
}
}
void sort(int arr[], int l, int r)
{
if(l<r){
int m = (l+r)/2;
sort(arr, l, m);
sort(arr, m+1, r);
merge(arr, l, m, r);
}
}
void merge(int arr[], int l, int m, int r)
{
//find out helper array length
int helper[] = new int[r-l+1];
// Your code here
for(int i=l; i<=r; i++){
helper[i] = arr[i];
}
int i = l;
int k = l;
int j = m+1;
while(i<=m && j<=r){
if(helper[i]<=helper[j]){
arr[k]=helper[i];
i++;
}else{
arr[k]=helper[j];
j++;
}
k++;
}
while(i<=m){
arr[k]=helper[i];
i++;
k++;
}
}
}
If I understand the way you're attempting to implement the MERGE subroutine for your merge sort algorithm correctly, you're copying the relevant portion of the array (from index l, inclusive, to index l, inclusive) into the helper array, then modifying the original array using the data copied into the helper array.
In this case, the helper array should indeed have length r-l+1. However, taking a look at your code for copying the original array portion into the helper array, you're not offsetting your indexes in the for loop. The correct way to do it would be:
int helper[] = new int[r-l+1];
for (int i = l; i <= r; i++){
helper[i-l] = arr[i];
}
Don't forget arrays are zero indexed!
I do however recommend moving on to more modern ways of copying arrays if you absolutely have to copy them. In this case, the most adapted in my opinion would be the system call System.arraycopy(arr, l, helper, 0, r-l+1). If you would like to read more about the different methods of copying an array (completely or partially) in Java, check this answer out.
You create an array with the size r - l + 1
int helper[] = new int[r-l+1];
The merge method will be executed with l = 3, m = 3, r = 4
So the array helper has a size of r - l + 1 = 2 = [0, 0]
Your for loop runs from l..r
for(int i=l; i<=r; i++){
helper[i] = arr[i];
}
And you try to set the value from arr[3] to helper[3] which is not possible because helper.length = 2
That's why the ArrayIndexOutOfBoundsException occurs! You can modfiy the access to the helper index to i - l
void merge(int arr[], int l, int m, int r) {
// find out helper array length
int helper[] = new int[r - l + 1];
// Your code here
System.arraycopy(arr, l, helper, 0, helper.length);
int i = l;
int k = l;
int j = m + 1;
while (i <= m && j <= r) {
if (helper[i - l] <= helper[j - l]) { // <---
arr[k] = helper[i - l]; // <---
i++;
} else {
arr[k] = helper[j - l]; // <---
j++;
}
k++;
}
while (i <= m) {
arr[k] = helper[i - l]; // <---
i++;
k++;
}
}

Recursive Quick Sort in java

This is my quicksort Code. It gives me a wrong answer but i think my partition function is correct.
public class Quick_Sort {
public static void main(String[] args)
{
int a[] = {99,88,5,4,3,2,1,0,12,3,7,9,8,3,4,5,7};
quicksort(a, 0, a.length-1);
}
static int partition(int[] a, int low , int hi)
{
int pivot = hi;
int i =low;
int j = hi-1;
while(i<j)
{
if(a[i]<=a[pivot])
{
i++;
}
if(a[i]>a[pivot])
{
if((a[i]>a[pivot]) && (a[j]<=a[pivot]))
{
int temp= a[i];
a[i]=a[j];
a[j]=temp;
i++;
}
if(a[j]>a[pivot])
{
j--;
}
}
}
int temp= a[i];
a[i]=a[pivot];
a[pivot]=temp;
return i;
}
static void quicksort(int[] a, int low, int hi)
{
if(low>=hi)
{
return;
}
int split = partition(a, low, hi);
quicksort(a, low, split-1);
quicksort(a, split+1, hi);
}
}
This is the final output:
1 0 3 2 3 4 4 5 5 7 3 7 8 9 12 88 99
Tried dry running it, couldn't see the error
In your partition method you have assigned j to hi - 1. It should be set to hi only.
static int partition(int[] a, int low , int hi)
{
int pivot = hi;
int i =low;
// int j = hi-1; // CHANGE THIS TO
int j = hi; // THIS
while(i<j)
I got this output after I made that change:
[0, 1, 2, 3, 3, 3, 4, 4, 5, 5, 7, 7, 8, 9, 12, 88, 99]
Hope this helps!
This is C# code:
public void RecursiveQuickSort(int[] array, int start, int end)
{
if (start < end)
{
int pivot = start;
int left = start + 1;
int right = end;
while (true)
{
while (array[left] <= array[pivot] && left < right)
left++;
while (array[right] > array[pivot] && left < right)
right--;
if (left < right)
{
Swap(array, left, right);
}
else
{
pivot = (array[pivot] > array[left]) ? left : left - 1;
Swap(array, start, pivot);
RecursiveQuickSort(array, start, pivot - 1);
RecursiveQuickSort(array, pivot + 1, end);
return;
}
}
}
}
private void Swap(int[] array, int index1, int index2)
{
int temp = array[index1];
array[index1] = array[index2];
array[index2] = temp;
}
This is a very good implementation and is much more Java standard. Maybe you want to take a look here, obviously all credits go to the original author.
http://www.vogella.com/tutorials/JavaAlgorithmsQuicksort/article.html
this is how I made it C:
private static List<Integer> recursive_quick_sort (List<Integer> list){
if (list.size()< 2){
return list;
}else {
int pivot = list.get(0);
List<Integer> less = list.stream().filter(element -> element < pivot ).collect(Collectors.toList());
List <Integer> greater = list.stream().filter(element -> element > pivot ).collect(Collectors.toList());
List<Integer> newList = Stream.of(recursive_quick_sort(less),List.of(pivot),recursive_quick_sort(greater))
.flatMap(Collection::stream)
.collect(Collectors.toList());
return newList;
}
}

Quicksort Counter Example

Firstly (as the question title implies) I'm not looking for why the bellow partitioning method doesn't work, rather a modification to it so that it will work for the following input:
int[] a = {1,2,8,4,5,7};
Here's the partition method along with some other stuff:
static int[] swap (int[] a,int i,int j){
int t = a[i];
a[i] = a[j];
a[j] = t;
return a;
}
static int partition (int[] a,int l,int r){
int i = l;
int j = r;
int v = a[l];
while (true) {
while (a[i] < v) {
if (i == r) break;
i++;
}
while (a[j] > v) {
if (j == l) break;
j--;
}
if (i >= j) break;
a = swap(a,i,j);
}
a = swap(a, l, j);
return j;
}
void sort(int[] a,int l,int r){
int j = partition(a, l, r);
sort(a, l, j-1);
sort(a, j+1, r);
}
public static void main(String[] args) {
int[] a = {1,2,8,4,5,7};
System.out.println(partition(a,0,5));
}
Output:
0
The output is the index of the pivot returned from the partition method. 0, as the index of the pivot, makes sense in terms of the definition, i.e. everything left of the pivot is smaller and everything right of the pivot is larger, but clearly runs into a problem in sort namely:
sort(a, l, j-1);
where you have the right pointer being negative (j-1 = 0-1 = -1). My question again being is there a modification to the above method(s) that will maintain the definition (everything left of the pivot is smaller and everything right of the pivot is larger) and not run into the problem in sort.
The missing part is the line
if ( l >= r ) return;
in the beginning of the sort method. This is actually the recursion stop step so it is necessary to have it anyway to prevent endless recursion. But besides that, it also solves your problem, because if you call sort(0,-1) then -1 is less than 0, so it prevents further processing of that index.

Quick sort imlpementation in Java-wrong output

I am trying to implement quick sort using Java.The partition function does what it should do.That is,partition the array around the pivot(I've chosen the element as the pivot). But the final output is not in sorted order.I cannot figure the error out.Can someone help?
public class Quick_sort {
public static int arr[] = {11, 2, 7, 1, 5, 4, 12, 65, 23};
public static int temp = 0;
public static void main(String args[]) {
int p=0;
int r=arr.length;
quick_sort(p,r);
for(int i: arr)
System.out.println(i);
}
public static int partition(int p, int r) {
if(p < r) {
int pivot=arr[p];
int i=1;
for(int j=1;j<r;j++) {
if(arr[j]<pivot) {
temp=arr[j];
arr[j]=arr[i];
arr[i]=temp;
i++;
}
}
temp=arr[i-1];
arr[i-1]=arr[p];
arr[p]=temp;
for(int m=0;m<r;m++) {
if(arr[m]==pivot) {
temp=m;
}
}
}
return temp;
}
public static void quick_sort(int p,int r) {
if(p>=r) return;
int index=partition(p,r);
quick_sort(p,index-1);
quick_sort(index+1,r-1);
}
}
In your last line
quick_sort(index+1,r-1);
You skip the last element of the array. But the last element should be sorted as well. Try it with:
quick_sort(index+1,r);
And it is better to adapt the variables i and j in the partition method to the current processed part of the array.
So I tried to fix it. Try it with (main function):
int r=arr.length-1;
and change the partition function to:
public static int partition(int p, int r) {
if(p < r) {
int pivot=arr[p];
int i= p ;
for(int j=(p+1);j<=r;j++) {
if(arr[j]<pivot) {
temp=arr[j];
arr[j]=arr[i + 1];
arr[i + 1] = arr[i];
arr[i] = temp;
i++;
}
}
temp = i;
}
return temp;
}
as well as the in the quick-sort method:
quick_sort(p,index-1);
quick_sort(index+1,r);
Do you see your problem? Your main problem was to not adapt the variables to the smaller parts you are actually looking at at the moment. It did well for the first partition round, but not for the following, as you had the former variables.
This is a complete example of a QuickSort implementation :
public class QuickSort {
public static void main(String[] args) {
int[] x = { 9, 2, 4, 7, 3, 7, 10 };
System.out.println(Arrays.toString(x));
int low = 0;
int high = x.length - 1;
quickSort(x, low, high);
System.out.println(Arrays.toString(x));
}
public static void quickSort(int[] arr, int low, int high) {
if (arr == null || arr.length == 0)
return;
if (low >= high)
return;
// pick the pivot
int middle = low + (high - low) / 2;
int pivot = arr[middle];
// make left < pivot and right > pivot
int i = low, j = high;
while (i <= j) {
while (arr[i] < pivot) {
i++;
}
while (arr[j] > pivot) {
j--;
}
if (i <= j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
i++;
j--;
}
}
// recursively sort two sub parts
if (low < j)
quickSort(arr, low, j);
if (high > i)
quickSort(arr, i, high);
}
}
you can find more here.

big difference in time between two Implementation of quick Sort

I have two implementation of quick sort the first one uses a median of (fist ,middle , last ) as pivot and the second uses the middle element as pivot
the first Implementation :
public class quickMedian {
public void sort(int array[])
// pre: array is full, all elements are non-null integers
// post: the array is sorted in ascending order
{
quickSort(array, 0, array.length - 1); // quicksort all the elements in the array
}
public void quickSort(int array[], int start, int end)
{
int i = start; // index of left-to-right scan
int k = end; // index of right-to-left scan
if (end - start >= 1) // check that there are at least two elements to sort
{
if (array[start+(end-start)/2]>array[end]){
swap(array,start+(end-start)/2, end);
}
if (array[start]>array[end]){
swap(array,start, end);
}
if (array[start+(end-start)/2]>array[start]){
swap(array, start+(end-start)/2, start);
}
int pivot = array[start]; // set the pivot as the first element in the partition
while (k > i) // while the scan indices from left and right have not met,
{
while (array[i] <= pivot && i <= end && k > i) // from the left, look for the first
i++; // element greater than the pivot
while (array[k] > pivot && k >= start && k >= i) // from the right, look for the first
k--; // element not greater than the pivot
if (k > i) // if the left seekindex is still smaller than
swap(array, i, k); // the right index, swap the corresponding elements
}
swap(array, start, k); // after the indices have crossed, swap the last element in // the left partition with the pivot
quickSort(array, start, k - 1); // quicksort the left partition
quickSort(array, k + 1, end); // quicksort the right partition
}
else // if there is only one element in the partition, do not do any sorting
{
return; // the array is sorted, so exit
}
}
public void swap(int array[], int index1, int index2)
// pre: array is full and index1, index2 < array.length
// post: the values at indices 1 and 2 have been swapped
{
int temp = array[index1]; // store the first value in a temp
array[index1] = array[index2]; // copy the value of the second into the first
array[index2] = temp; // copy the value of the temp into the second
}
}
The second Implementation :
public class quickSort {
private int array[];
private int length;
public void sort(int[] inputArr) {
if (inputArr == null || inputArr.length == 0) {
return;
}
this.array = inputArr;
length = inputArr.length;
quickSorter(0, length - 1);
}
private void quickSorter(int lowerIndex, int higherIndex) {
int i = lowerIndex;
int j = higherIndex;
// calculate pivot number, I am taking pivot as middle index number
int pivot = array[lowerIndex+(higherIndex-lowerIndex)/2];
// Divide into two arrays
while (i <= j) {
/**
* In each iteration, we will identify a number from left side which
* is greater then the pivot value, and also we will identify a number
* from right side which is less then the pivot value. Once the search
* is done, then we exchange both numbers.
*/
while (array[i] < pivot) {
i++;
}
while (array[j] > pivot) {
j--;
}
if (i <= j) {
exchangeNumbers(i, j);
//move index to next position on both sides
i++;
j--;
}
}
// call quickSort() method recursively
if (lowerIndex < j)
quickSorter(lowerIndex, j);
if (i < higherIndex)
quickSorter(i, higherIndex);
}
private void exchangeNumbers(int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
To obtain the median we need to do extra steps on each recursion which may increase the time a little bit (if the array is totally random) .
I am testing these two classes on an array of size N=10,000,000 randomly generated and I have done the test many times the first Implementation takes around 30 seconds and the second takes around 4 seconds
so this is obviously not caused by the extra overhead to get the median of three numbers .
There must be something wrong with the first implementation, what is it ?
here is the testing code :
public static void main(String[] args) {
File number = new File ("f.txt");
final int size = 10000000;
try{
// quickSort s = new quickSort();
quickMedian s = new quickMedian();
writeTofile(number, size);
int [] arr1 =readFromFile(number, size);
long a=System.currentTimeMillis();
s.sort(arr1);
long b=System.currentTimeMillis();
System.out.println("quickSort: "+(double)(b-a)/1000);
}catch (Exception ex){ex.printStackTrace();}
}

Categories

Resources