Trying to implement merge sort in Java. I've gone over my code a bunch in my head and I feel like it should be working, but obviously I'm doing something wrong. Heres the code
public static void mergeSort(int[] input, int start, int end) {
if (end - start < 2)
return;
int mid = (start + end) / 2;
mergeSort(input, start, mid);
mergeSort(input, mid + 1, end);
merge(input, start, mid, end);
}
public static void merge(int[] input, int start, int mid, int end) {
if (input[mid - 1] <= input[mid])
return;
int i = start;
int j = mid;
int tempIndex = 0;
int[] temp = new int[end - start]; //combined size of both arrays being merged
/*if i is >= mid, then that means the left portion of the array is done being sorted
and vice versa if j >= end. Once this happens, we should be able to
just copy the remaining elements into the temp array
*/
while (i < mid && j < end) {
temp[tempIndex++] = (input[i] <= input[j]) ? input[i++] : input[j++];
}
//Copying left over elements in left portion
while (i < mid)
temp[tempIndex++] = input[i++];
//Copying left over elements in right portion
while (j < end)
temp[tempIndex++] = input[j++];
//Copy the sorted temp array into the original array
//This is where I think I must be messing up
for (int k = 0; k < temp.length; k++) {
input[start + k] = temp[k];
}
}
I think it must be that im not copying correctly the temp array with the sorted elements back into the original array, but I'm not sure. I wrote comments on my code explaining my logic.
Take a look at the following changes:
Calculating mid
int mid = start + (end - start) / 2;
Assigning pointers i,j correctly.
int i = start;
int j = mid+1;
Correct size of temp array.
int [] temp = new int[end-start+1];
Corrected while loops condition in the code.
class Solution{
public static void mergeSort(int[] input, int start, int end)
{
if (end == start ) return;
int mid = start + (end - start) / 2;
mergeSort(input, start, mid);
mergeSort(input, mid+1, end);
merge(input, start, mid, end);
}
public static void merge(int[] input, int start, int mid, int end) {
// No Need of the under mentioned instruction
// if(input[mid-1] <= input[mid]) return;
int i = start;
int j = mid+1;
int tempIndex = 0;
int [] temp = new int[end-start+1]; //combined size of both arrays being merged
/*if i is >= mid, then that means the left portion of the array is done being sorted and vice versa if j >= end. Once this happens, we should be able to just copy the remaining elements into the temp array */
while(i <= mid && j <= end){
temp[tempIndex++] = (input[i] <= input[j]) ? input[i++] : input[j++];
}
//Copying left over elements in left portion
while(i <= mid)
temp[tempIndex++] = input[i++];
//Copying left over elements in right portion
while(j <= end)
temp[tempIndex++] = input[j++];
//Copy the sorted temp array into the original array
//This is where I think I must be messing up
for(int k = 0; k < temp.length; k++){
input[start+k] = temp[k];
}
}
public static void main(String[] args){
int[] input = {9,4,6,8,5,7,0,2};
mergeSort(input,0,7);
for(int i : input)
System.out.println(i);
}
}
Related
This question already has answers here:
What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?
(26 answers)
Closed 1 year ago.
this is the QuickSort Randomized that I've come up with, but it constantly throws out IndexOutOfBounds exception. Could I have some help with it? Thanks!
import java.util.Random;
public class QuickSort {
void quickSort(int[] A, int start, int end) { // Initially: start = 0, end = n-1
while (start < end) {
int iOfPartition = randomisedPartition(A, start, end);
if (iOfPartition - start < end - iOfPartition) {
quickSort(A, start, iOfPartition - 1);
start = iOfPartition + 1;
} else {
quickSort(A, iOfPartition + 1, end);
end = iOfPartition - 1;
}
}
}
int randomisedPartition(int[] A, int start, int end) {
Random rng = new Random();
int randomNum = rng.nextInt(end + 1 - start) + start;
swap(A, A[randomNum], A[start]);
return hoarePartition(A, start, end);
}
int hoarePartition(int[] A, int start, int end) {
int pivot = A[start];
int i = start;
int j = end;
while (i < j) {
while (A[i] <= pivot && i < end) i++;
while (A[j] > pivot && j > start) j--;
if (i < j) swap(A, A[i], A[j]);
}
swap(A, A[start], A[j]);
return j;
}
void swap(int[] A, int i, int j) {
int temp = A[i];
A[i] = A[j];
A[j] = temp;
}
}
I keep getting an arrayindexoutofbounds error.
I echo the sentiment of the comment above, you should learn to use the debugger or print statements to try to piece together what is happening.
Still, I couldn't resist investigating.
Check out what you are doing here in the call to swap. You are taking the value which is in the randomNum position with A[randomNum]
swap(A, A[randomNum], A[start]); // incorrectly indexing here
But then inside swap, you are repeating the process, and taking the value at the A[A[randomNum]] which does not necessarily exist.
int temp = A[i]; // indexing here again
So your problem is that you are incorrectly indexing twice. You should use [] only in the swap function, not in the randomisedPartition function. randomisedPartition should send swap indexes, not indexed values.
How did I figure this out ?
I tried a call with very simple data
int data[] = {5,3,4};
new Example().quickSort(data, 0, 2);
and got an index out of bounds 5 error. That's how you debug.
Attempting to implement QuickSort using Hoare Partition Scheme, but I am running into a problem where changing the index of the pivot causes overflow, regardless of array size. Code:
public void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public int partition(int[] l, int min, int max){
int pivot = l[min];
int i = min - 1;
int j = max +1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
//Swap
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
This implementation chooses the low-index (named min here) as the pivot element, and this works just fine. However, changing the pivot element to any other index, causes a StackOverflow Error regardless of the size of the array that is being sorted. (Error refers to line 3, where partition() is called) I would preferably have the pivot element chosen at random within the (min,max) range. What is causing this?
EDIT:
The array used is generated as follows:
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
In one of the Overflow cases I used this:
genereateRandomArray(10, 0, 9);
For some concrete examples, running the code above but changing the pivot element to say, l[max-1] or l[min+1], l[min+2] etc gives StackOverflow on my end.
The solution to my problem was as user MBo pointed out to swap the pivot element to the first index of the array, as the algorithm itself relies on the pivot being on index 0. This is what I had overlooked.
(int i = min - 1; is correct however, and stays that way.)
We can see that at the first step i becomes equal to min, comparison of pivot element with itself fails and increment does not occur more:
int pivot = l[min];
int i = min - 1;
...
do{
i++;
}while(l[i] < pivot);
Exclude pivot element from comparison (int i = min;) and exchange it with partition one (seems l[j]) at the end
Using the middle value for pivot is working for me. Here is a complete example:
public static void quickSort(int[] l, int min, int max){
if (min < max){
int p = partition(l, min, max);
quickSort(l, min, p);
quickSort(l, p+1, max);
}
}
public static int partition(int[] l, int min, int max){
int pivot = l[(min+max)/2];
int i = min - 1;
int j = max + 1;
while(true){
do{
i++;
}while(l[i] < pivot);
do{
j--;
}while(l[j] > pivot);
if (i >= j) {
return j;
}
int temp = l[i];
l[i] = l[j];
l[j] = temp;
}
}
public static int[] generateRandomArray(int size, int lower, int upper){
int[] random = new int[size];
for (int i = 0; i < random.length; i++) {
int randInt = ThreadLocalRandom.current().nextInt(lower, upper+1);
random[i] = randInt;
}
return random;
}
public static void main(String[] args) {
int[] A = generateRandomArray(10, 0, 9);
long bgn, end;
bgn = System.currentTimeMillis();
quickSort(A, 0, A.length-1);
end = System.currentTimeMillis();
for(int i = 1; i < A.length; i++){
if(A[i-1] > A[i]){
System.out.println("failed");
break;
}
}
System.out.println("milliseconds " + (end-bgn));
}
Sorry, beginner here.This is what I have right now:
public class MergeSort
{
public static void main(String[] args)
{
int[] arr = {3, 5, 2, 4, 1};
sort(arr, 0, arr.length - 1);
for(int i = 0; i < arr.length; i++)
{
System.out.print(arr[i] + " ");
}
}
private static void sort(int[] arr, int lo, int hi)
{
if(lo >= hi)
{
return;
}
int mid = (lo + hi)/2;
sort(arr, lo, mid);
sort(arr, mid + 1, hi);
int size = hi - lo + 1;
int[] temp = new int[size]; //new array to merge into
merge(arr, temp, lo, mid + 1, hi);
for(int i = 0; i < size; i++)
{
arr[i + lo] = temp[i];
}
}
private static void merge(int[] arr, int[] temp, int lower, int mid, int upper)
{
int tempIndex = 0;
int leftLo = lower;
int leftHi = mid - 1;
int rightLo = mid;
int rightHi = upper;
while(leftLo <= leftHi && rightLo <= rightHi)
{
if(arr[leftLo] < arr[rightLo])
{
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
else
{
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
}
}
I know it's the merge function that is not working, because right now it prints out only the smallest element and the rest as 0's. I think it has something to do with needing another while loop to copy the array, but I don't know how to write that, or even the purpose of it, as right now it seems that the array is being merged into the temp array in a correct order. Why is it only printing the first element correctly? Thanks.
In merge, you copy values as long as leftLo and rightLo both haven't reached their limit yet. Typically one of them reaches early. Then you need to copy the remaining values of the other one. You can copy the remaining elements by adding these two loops:
while (leftLo <= leftHi) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
while (rightLo <= rightHi) {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
That is, the complete method becomes:
private static void merge(int[] arr, int[] temp, int lower, int mid, int upper) {
int tempIndex = 0;
int leftLo = lower;
int leftHi = mid - 1;
int rightLo = mid;
int rightHi = upper;
while (leftLo <= leftHi && rightLo <= rightHi) {
if (arr[leftLo] < arr[rightLo]) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
} else {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
while (leftLo <= leftHi) {
temp[tempIndex] = arr[leftLo];
tempIndex++;
leftLo++;
}
while (rightLo <= rightHi) {
temp[tempIndex] = arr[rightLo];
tempIndex++;
rightLo++;
}
}
I've been banging my head on the table on this one.
I need to create an n sized array that is optimized for QuickSort Partition. It will be used to demonstrate the growth of QuickSort's best case. I know that for best case, QuickSort must select a pivot that divides the array in half for every recursive call.
I cannot think of a way to create an n-sized optimized array to test. Any help would be greatly appreciated.
Here is the algorithm in Java.
public class QuickSort {
private int length;
private void quickSort(int[] a, int p, int r) {
if (p < r) {
int q = partition(a, p, r);
quickSort(a, p, q - 1);
quickSort(a, q + 1, r);
}
}
private int partition(int[] a, int p, int r) {
int x = a[r];
int i = p - 1;
for (int j = p; j < r; j++) {
if (a[j] <= x) {
i++;
exchange(a, i, j);
}
}
exchange(a, i + 1, r);
return i + 1;
}
public void exchange(int[] a, int i, int j) {
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
QuickSort(int[] a) {
if (a == null || a.length == 0) {
return;
}
length = a.length;
quickSort(a, 0, length - 1);
}
}
I know this is an old question, but I had the same question and finally developed a solution. I'm not a Java programmer, so don't blame me for Java code issues, please. I assumed that the quicksort algorithm always takes the first item as a pivot when partitioning.
public class QuickSortBestCase
{
public static void generate(int[] arr, int begin, int end)
{
int count = end - begin;
if(count < 3)
return;
//Find a middle element index
//This will be the pivot element for the part of the array [begin; end)
int middle = begin + (count - 1) / 2;
//Make the left part best-case first: [begin; middle)
generate(arr, begin, middle);
//Swap the pivot and the start element
swap(arr, begin, middle);
//Make the right part best-case, too: (middle; end)
generate(arr, ++middle, end);
}
private static void swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
private static void fillArray(int[] arr)
{
for(int i = 0; i != arr.length; ++i)
arr[i] = i + 1;
}
private static void printArray(int[] arr)
{
for(int item : arr)
System.out.print(item + " ");
}
public static void main(String[] args)
{
if(args.length == 0)
return;
int intCount = Integer.parseInt(args[0]);
int[] arr = new int[intCount];
//We basically do what quicksort does in reverse
//1. Fill the array with sorted values from 1 to arr.length
fillArray(arr);
//2. Recursively generate the best-case array for quicksort
generate(arr, 0, arr.length);
printArray(arr);
}
}
This program produces the same output for the array of 15 items, as described here: An example of Best Case Scenario for Quick Sort. And in case someone needs a solution in C++:
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case_sorted(RandomIterator begin, RandomIterator end)
{
auto count = std::distance(begin, end);
if (count < 3)
return;
auto middle_index = (count - 1) / 2;
auto middle = begin + middle_index;
//Make the left part best-case first
generate_quicksort_best_case_sorted(begin, middle);
//Swap the pivot and the start element
std::iter_swap(begin, middle);
//Make the right part best-case, too
generate_quicksort_best_case_sorted(++middle, end);
}
template<typename RandomIterator,
typename Compare = std::less<typename RandomIterator::value_type>>
void generate_quicksort_best_case(RandomIterator begin, RandomIterator end)
{
{
auto current = begin;
RandomIterator::value_type value = 1;
while (current != end)
*current++ = value++;
}
generate_quicksort_best_case_sorted(begin, end);
}
I'm trying to code a quicksort program in java.here is my partition function
int partition(int[] array, int start, int end)
{
int last = end - 1;
int first = start;
int pivot = array[start];
while (first < last)
{
while (first < last && pivot <= array[last])
last = last - 1;
array[first] = array[last];
while (first < last && pivot > array[first])
first = first + 1;
array[last] = array[first];
}
array[first] = pivot;
return first;
}
and that is my quicksort function
void quickSort(int array[], int start, int end) {
int index = partition(array, start, end);
if (start < index - 1)
quickSort(array, start, end - 1);
if (index < end)
quickSort(array, index, end);}
But when I test the code in Junit it gives me error. I need to change quickSort or partition function. What can I do with that.
Here:
if (start < index - 1)
quickSort(array, start, end - 1);
Why do you call quickSort for lower partition from start, to end-1? Shouldnt it be:
if (start < index - 1)
quickSort(array, start, index - 1);
And the upper part is from index+1 to end.
Moreover, it is better to check the limits like this before the partitioning and remove your if statements:
if (end <= start) { return; }
To me, it just seems much less intuitive to write the functions like those above. Take this sample code that I just wrote (and tested) as an example, and see if you can understand it and use it to help you through your code. NB: This is slightly different than yours in that it picks a pivot at random.
void quicksort(int[] nums)
{
if (nums.length > 1)
{
quicksort(nums, 0, nums.length);
}
}
//[left,right)
private void quicksort(int[] nums, int left, int right)
{
if (left < right - 1)
{
int position = left;
int pivot = pickPivot(left, right);
if (pivot != right - 1)
swap(nums, pivot, right-1);
for (int i = left; i < right - 1; i++)
{
if (nums[i] <= nums[right-1])
{
swap(nums, position, i);
position++;
}
}
swap(nums, position, right-1);
quicksort(nums, left, position);
quicksort(nums, position + 1, right);
}
}
//[left,right)
private int pickPivot(int left, int right)
{
return rand.nextInt(right-left) + left; // rand is a Random object
}
private void swap(int[] nums, int start, int end)
{
int temp = nums[end];
nums[end] = nums[start];
nums[start] = temp;
}
As others have said, it can be difficult to debug recursive functions. If you can't figure it out from here, I suggest using a debugger. Eclipse's debugger is pretty easy to use and very helpful. This may be frustrating and difficult to figure out, but the process is one that every programmer (should and) has to go through.
Once you think you have it figured out, use a testing function similar to this one to test it out (I would initially start off with a smaller size in case there are errors, this way it will be easier to debug):
void testQuickSort()
{
for(int i = 0; i < 1000; i++)
{
int size = rand.nextInt(100)+1;
int[] nums = new int[size];
for (int j = 0; j < size; j++)
{
nums[j] = rand.nextInt(10);
if (rand.nextBoolean())
{
nums[j] *= -1;
}
}
quicksort(nums);
assert sorted(nums) == true;
}
}
private boolean sorted(int[] array)
{
for (int i = 0; i < array.length-1; i++)
{
if (array[i] > array[i+1])
return false;
}
return true;
}
stack overflow error on line "quickSort(array, index, end);
When array is big enough recusion call too many times the same function overflowing stack.
Instead of recursion place pairs of indexes in a stack and call the quickSort() for top pair.
void quickSort(int array[], int start, int end) {
int index = partition(array, start, end);
if (start < index - 1)
// quickSort(array, start, end - 1);
push(start, end - 1);
if (index < end)
// quickSort(array, index, end);}
push(index, end);
while(stack is not empty) {
//get next pair and call quickSort
}