I am trying make an iterative version of Merge Sort for an Assignment. I got the Merge sort method from a website, and I worked on the method that is supposed to merge the arrays. However I keep getting an IndexOutOfBounds Exception.
I have been working on this for multiple hours and I cannot find the error. Can someone help me find a way to solve this?
So far I have this:
public static void MergeSort(int[] array) {
int current;
int leftStart;
int arraySize = array.length - 1;
for (current = 1; current <= arraySize; current = 2 * current) {
for (leftStart = 0; leftStart <= arraySize; leftStart += 2 * current) {
int mid = leftStart + current - 1;
int right = getMin(leftStart + 2 * current - 1, arraySize);
mergeArray(array, leftStart, mid, right);
}
}
}
public static void mergeArray(int[] array, int left, int mid, int right) {
int leftArraySize = mid - left + 1;
int rightArraySize = right - mid;
int[] leftArray = new int[leftArraySize];
int[] rightArray = new int[rightArraySize];
for (int i = 0; i < leftArraySize; i++)
leftArray[i] = array[left + i];
for (int i = 0; i < rightArraySize; i++)
rightArray[i] = array[mid + 1 + i];
int leftPtr = 0;
int rightPtr = 0;
int tempPtr = leftPtr;
while (leftPtr < leftArraySize && rightPtr < rightArraySize) {
if (leftArray[leftPtr] <= rightArray[rightPtr])
array[tempPtr++] = leftArray[leftPtr++];
else
array[tempPtr++] = rightArray[rightPtr++];
}
while (leftPtr <= left)
array[tempPtr++] = leftArray[leftPtr++];
while (rightPtr < right)
array[tempPtr++] = rightArray[rightPtr++];
}
public static int getMin(int left, int right) {
if (left <= right) {
return left;
} else {
return right;
}
}
Any sort of help will be highly appreciated!
Thanks!
Merge sort algorithm is a classical Divide and Conquer algorithm.
Divide the problem into smaller sub problems
Conquer via recursive calls.
Combine solutions of sub problems into one for the original problem
The Pseudocode for Merge:
C = output[length = n]
A = 1st sorted array[n/2]
B = 2st sorted array[n/2]
i = 1
j = 1
for k = 1 to n
if A[i] < B[j]
C[k] = A[i]
i++
else B[j]<A[i]
C[k] = B[j]
j++
end (ignores end cases)
So your source code problem is this line:
array[tempPtr++] = leftArray[leftPtr++];
please change to the logic of pseudocode:
if (leftArray [leftPtr ] <= rightArray[rightPtr ])
{
array[tempPtr] = leftArray [leftPtr];
leftPtr++;
}
else
{
array[tempPtr] = rightArray[rightPtr];
rightPtr++;
}
Try this code Successfully executed:
Only Small mistake in mergeArray() method:
array[tempPtr++] = leftArray[leftPtr++];
DOn't increment in array... Replace
array[tempPtr] = leftArray [leftPtr];
leftPtr++;
Final code: Compare my code you will get it.
public static void MergeSort(int[] array) {
int current;
int leftStart;
int arraySize = array.length;
for (current = 1; current <= arraySize-1; current = 2 * current) {
for (leftStart = 0; leftStart < arraySize-1; leftStart += 2 * current) {
int mid = leftStart + current - 1;
int right = getMin(leftStart + 2 * current - 1, arraySize-1);
mergeArray(array, leftStart, mid, right);
}}}
static void printArray(int A[])
{
int i;
for (i=0; i < A.length; i++)
System.out.println(A[i]);
}
static void mergeArray(int array[], int left, int mid, int right)
{
int leftArraySize = mid - left + 1;
int rightArraySize = right - mid;
int[] leftArray = new int[leftArraySize];
int[] rightArray = new int[rightArraySize];
for (int i = 0; i < leftArraySize ; i++)
leftArray [i] = array[left + i];
for (int j = 0; j < rightArraySize; j++)
rightArray[j] = array[mid + 1+ j];
int leftPtr = 0;
int rightPtr = 0;
int tempPtr = left;
while (leftPtr < leftArraySize && rightPtr < rightArraySize)
{
if (leftArray [leftPtr ] <= rightArray[rightPtr ])
{
array[tempPtr] = leftArray [leftPtr];
leftPtr++;
}
else
{
array[tempPtr] = rightArray[rightPtr];
rightPtr++;
}
tempPtr++;
}
while (leftPtr < leftArraySize )
{
array[tempPtr++] = leftArray [leftPtr++];
leftPtr++;
tempPtr++;
}
while (rightPtr < rightArraySize)
{
array[tempPtr++] = rightArray[rightPtr++];
rightPtr++;
tempPtr++;
} }
public static int getMin(int left, int right) {
if (left <= right) {
return left;
} else {
return right;
}}
Related
I am working on sorting algorithms and I am trying to improve mergeSort by locating already sorted subArrays.
public static void mergeSort(int[] array)
{
if(array == null)
{
return;
}
if(array.length > 1)
{
int mid = array.length / 2;
// left
int[] left = new int[mid];
for(int i = 0; i < mid; i++)
{
left[i] = array[i];
}
//right
int[] right = new int[array.length - mid];
for(int i = mid; i < array.length; i++)
{
right[i - mid] = array[i];
}
//recursively calls
mergeSort(left);
mergeSort(right);
int i = 0;
int j = 0;
int k = 0;
// left and right merged
while(i < left.length && j < right.length)
{
if(left[i] < right[j])
{
array[k] = left[i];
i++;
}
else
{
array[k] = right[j];
j++;
}
k++;
}
// left overs
while(i < left.length)
{
array[k] = left[i];
i++;
k++;
}
while(j < right.length)
{
array[k] = right[j];
j++;
k++;
}
}
}
What you are looking for is called natural merge sort. Before you start with sorting the dataset you will do one run to identify all the presorted data. The mergesort itself stays the same.
I found some example code for you at: happycoders
package eu.happycoders.sort.method.mergesort;
import eu.happycoders.sort.method.Counters;
import eu.happycoders.sort.method.SortAlgorithm;
/**
* Natural merge sort implementation for performance tests.
*
* #author Sven Woltmann
*/
public class NaturalMergeSort implements SortAlgorithm {
#Override
public void sort(int[] elements) {
int numElements = elements.length;
int[] tmp = new int[numElements];
int[] starts = new int[numElements + 1];
// Step 1: identify runs
int runCount = 0;
starts[0] = 0;
for (int i = 1; i <= numElements; i++) {
if (i == numElements || elements[i] < elements[i - 1]) {
starts[++runCount] = i;
}
}
// Step 2: merge runs, until only 1 run is left
int[] from = elements;
int[] to = tmp;
while (runCount > 1) {
int newRunCount = 0;
// Merge two runs each
for (int i = 0; i < runCount - 1; i += 2) {
merge(from, to, starts[i], starts[i + 1], starts[i + 2]);
starts[newRunCount++] = starts[i];
}
// Odd number of runs? Copy the last one
if (runCount % 2 == 1) {
int lastStart = starts[runCount - 1];
System.arraycopy(from, lastStart, to, lastStart,
numElements - lastStart);
starts[newRunCount++] = lastStart;
}
// Prepare for next round...
starts[newRunCount] = numElements;
runCount = newRunCount;
// Swap "from" and "to" arrays
int[] help = from;
from = to;
to = help;
}
// If final run is not in "elements", copy it there
if (from != elements) {
System.arraycopy(from, 0, elements, 0, numElements);
}
}
private void merge(int[] source, int[] target, int startLeft,
int startRight, int endRight) {
int leftPos = startLeft;
int rightPos = startRight;
int targetPos = startLeft;
// As long as both arrays contain elements...
while (leftPos < startRight && rightPos < endRight) {
// Which one is smaller?
int leftValue = source[leftPos];
int rightValue = source[rightPos];
if (leftValue <= rightValue) {
target[targetPos++] = leftValue;
leftPos++;
} else {
target[targetPos++] = rightValue;
rightPos++;
}
}
// Copy the rest
while (leftPos < startRight) {
target[targetPos++] = source[leftPos++];
}
while (rightPos < endRight) {
target[targetPos++] = source[rightPos++];
}
}
#Override
public void sort(int[] elements, Counters counters) {
// Not implemented
}
}
I'm trying to implement quicksort using the median of three algo and it fails a unit test I wrote related to a small partition. I changed my earlier partition and now it passes one of the tests it used to fail, but still fails the one at the bottom:
My code is:
public class QuickSort {
static void swap(int[] A, int i, int j) {
int tmp = A[i];
A[i] = A[j];
A[j] = tmp;
}
static final int LIMIT = 15;
static int partition(int[] a, int left, int right){
int center = (left + right) / 2;
if(a[center] < (a[left]))
swap(a,left,center);
if(a[right-1] < a[left])
swap(a,left,right);
if(a[right-1] < a[center])
swap(a,center,right);
swap(a,center,right - 1);
return a[right - 1];
}
static void quicksort(int[] a, int left, int right){
if(left + LIMIT <= right){
int pivot = partition(a,left,right);
int i = left;
int j = right - 1;
for(;;){
while(a[++i] < pivot){}
while(a[--j] > pivot){}
if(i < j)
swap(a,i,j);
else
break;
}
swap(a,i,right - 1);
quicksort(a,left,i-1);
quicksort(a,i+1,right);
}
else{
insertionSort(a);
}
}
public static void insertionSort(int[] a){
int j;
for(int p = 1; p < a.length; p++){
int tmp = a[p];
for(j = p; j > 0 && tmp < a[j-1]; j--)
a[j] = a[j-1];
a[j] = tmp;
}
}
}
This test fails:
public void smalltest() throws Exception {
int[] Arr_orig = {3,9,8,2,4,6,7,5};
int[] Arr = Arr_orig.clone();
int pivot = QuickSort.partition(Arr, 0, Arr.length);
for (int i = 0; i != pivot; ++i)
assertTrue(Arr[i] <= Arr[pivot]); //fails!
for (int i = pivot + 1; i != Arr.length; ++i)
assertTrue(Arr[pivot] < Arr[i]);
}
There is a conflict between using right-1 and right in this sequence:
if(a[right-1] < a[left])
swap(a,left,right);
You need to decide if right is going to be the index to the last element, or 1 + index to the last element (an "ending" index).
There may be other problems, but this is the first one I noticed.
I'm in an algorithms course and am learning about merge sort. Our professor recommended we try to implement the pseudo code provided in the book.
Am I correct in using Integer.MAX_VALUE as a sentinel value when
sorting an array of integers (used in lines 8 & 9 in the Merge
method pseudo code below)?
For line 2 of the Merge-Sort pseudo code method, is it correct to code that in Java using Math.ceil() like I did? (Edit: It's actually floor and I updated my code to reflect this.)
If you see any other mistakes please let me know!
Here is the pseudo code the book gives for merge sort.
And, here is how I coded it in Java:
public void mergeSort(int[] arrNums, int p, int r) {
if (p < r) {
int q = (p + r) / 2;
mergeSort(arrNums, p, q);
mergeSort(arrNums, q + 1, r);
merge(arrNums, p, q, r);
}
}
public void merge(int[] arrNums, int p, int q, int r) {
int nOne = q - p + 1;
int nTwo = r - q;
int[] arrLeft = new int[nOne + 1];
int[] arrRight = new int[nTwo + 1];
for (int i = 0; i < nOne; i++) {
arrLeft[i] = arrNums[p + i - 1];
}
for (int j = 0; j < nTwo; j++) {
arrRight[j] = arrNums[q + j];
}
arrLeft[nOne] = Integer.MAX_VALUE;
arrRight[nTwo] = Integer.MAX_VALUE;
// Tracks arrLeft index
int i = 0;
// Tracks arrRight index
int j = 0;
for (int k = p; k < r; k++) {
if (arrLeft[i] <= arrRight[j]) {
arrNums[k] = arrLeft[i];
i++;
} else {
arrNums[k] = arrRight[j];
j++;
}
}
}
The last for loop in your merge method, variable k should start from p - 1:
for (int k = p - 1; k < r; k++) {
if (arrLeft[i] <= arrRight[j]) {
arrNums[k] = arrLeft[i];
i++;
} else {
arrNums[k] = arrRight[j];
j++;
}
}
Pseudo code in many text books likes to start array index from 1, so here you need to subtract it by 1.
I implemented it a few days ago, if someone will be interested.
private static void mergeSort(double[] arr, int start, int end){
if(start < end){
int mid = ( start + end ) / 2;
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
Merge(arr, start, mid, end);
}
}
private static void Merge(double[] arr, int start, int mid, int end){
double[] leftArray = new double[mid - start + 2];
double[] rightArray = new double[end - mid + 1];
for(int i = start; i <= mid; i++ )
leftArray[i - start] = arr[i];
for (int i = mid + 1; i <= end; i++ )
rightArray[i - mid - 1] = arr[i];
leftArray[mid - start + 1] = Double.POSITIVE_INFINITY;
rightArray[end - mid] = Double.POSITIVE_INFINITY;
int leftIndex = 0, rightIndex = 0;
for (int k = start; k <= end; k++){
if(leftArray[leftIndex] <= rightArray[rightIndex])
arr[k] = leftArray[leftIndex++];
else
arr[k] = rightArray[rightIndex++];
}
}
I am working on a non recursive merge sort for my CS class and it is not exactly working. I know it is being called since when I run the test program it changes the array, just not into the correct order. Can someone please help? Thanks!
private static void mergeSort(int[] a, int left, int right)
{
int midPoint = ((right + left) / 2);
int[] buffer = new int[19];
selectionSort(a, left, midPoint);
selectionSort(a, midPoint-1, right);
merge(a, buffer, 0, 9, 19);
}
private static void selectionSort(int[] a, int beginning, int end)
{
int [] temp = new int[end-1];
for(int y = 0; y < end - 1; y++)
{
temp[y] = a[y];
}
for (int i = 0; i < temp.length - 1; i++)
{
int minIndex = findMinimum(temp, i);
if (minIndex != i)
swap (temp, i, minIndex);
}
}
private static int findMinimum(int[] a, int first)
{
int minIndex = first;
for (int i = first + 1; i < a.length; i++)
{
if (a[i] < a[minIndex])
minIndex = i;
}
return minIndex;
}
private static void swap(int []a, int x, int y)
{
int temp = a[x];
a[x] = a[y];
a[y] = temp;
}
private static void merge(int[] a, int[] temp, int left, int mid, int right) {
if (mid >= a.length) return;
if (right > a.length) right = a.length;
int i = left, j = mid+1;
for (int k = left; k < right; k++) {
if (i == mid)
temp[k] = a[j++];
else if (j == right)
temp[k] = a[i++];
else if (a[j] < a[i])
temp[k] = a[j++];
else
temp[k] = a[i++];
}
for (int k = left; k < right; k++)
a[k] = temp[k];
}
There may be other bugs, but one that sticks out is that selectionSort doesn't actually do anything to the array. You pass in an array reference as the a parameter:
private static void selectionSort(int[] a, int beginning, int end)
Since this is a reference, if selectionSort did anything to assign to any elements of a, like
a[x] = y;
it would change the element of the caller's array, like you want. But there is no statement in selectionSort that changes anything in a. The code copies elements to temp, works with temp--but then throws all the work away.
Question: Where are comparisons being made in each separate sorting method?
Also if you know please tell me which method count numbers are wrong and where to place my counters instead.trying to understand where and how many times sorting methods make comparisons.
Method A B
Selection 4950 4950
Bubble 99 9900
Insertion 99 5049
Merge 712 1028
Shell 413 649
Quick 543 1041
Okay so to explain some parts, basically Array A is an array from 1-100 in ascending order. So this should be the minimum number of comparisons.
Array B is 100-1 in descending order. So I believe it should be the maximum number of comparisons. Array C is just randomly generated numbers, so it changes every time.
I feel like my selection and bubble sorts were counted correctly. Feel free to let me know where comparisons are being made that I haven't counted, or if I'm counting wrong comparisons.
Side note: Made some global variable to count the methods that were recursive in multiple sections.
class Sorting
{
static int[] X = new int[100];
static int mergecount = 0;
static int quickcount = 0;
public static void selectionSort(int list[])
{
int count = 0;
int position = 0, n = list.length;
for(int j = 0; j < n-1; j++)
{
position = j;
for(int k = j+1; k < n; k++)
{
count++;
if(list[k] < list[position])
position = k;
}
Swap(list, j, position);
}
System.out.println("counter" + count);
}
public static void Swap(int list[], int j, int k)
{
int temp = list[j];
list[j] = list[k];
list[k] = temp;
}
public static void bubbleSort(int list[])
{
int count = 0;
boolean changed = false;
do
{
changed = false;
for(int j = 0; j < list.length - 1; j++)
{
count++;
if(list[j] > list[j + 1])
{
Swap(list, j, j+1);
changed = true;
}
}
} while(changed);
System.out.println("counter" + count);
}
public static void insertionSort(int list[])
{
int count = 0;
for(int p = 1; p < list.length; p++)
{
int temp = list[p];
int j = p;
count++;
for( ; j > 0 && temp < list[j - 1]; j = j-1)
{
list[j] = list[j - 1];
count++;
}
list[j] = temp;
}
System.out.println("counter" + count);
}
public static void mergeSort(int list[])
{
mergeSort(list, 0, list.length - 1);
System.out.println("counter" + mergecount);
}
public static void mergeSort(int list[], int first, int last)
{
if(first < last)
{
int mid = (first + last) / 2;
mergeSort(list, first, mid);
mergeSort(list, mid + 1, last);
Merge(list, first, mid, last);
}
}
public static void Merge(int list[], int first, int mid, int last)
{
int count = 0;
int first1 = first, last1 = mid;
int first2 = mid + 1, last2 = last;
int temp[] = new int[list.length];
int index = first1;
while(first1 <= last1 && first2 <= last2)
{
if(list[first1] < list[first2])
{
temp[index] = list[first1++];
mergecount++;
}
else
temp[index] = list[first2++];
index++;
mergecount++;
}
while(first1 <= last1)
temp[index++] = list[first1++];
while(first2 <= last2)
temp[index++] = list[first2++];
for(index = first; index <= last; index++)
list[index] = temp[index];
}
public static void shellSort(int list[])
{
int count = 0;
int n = list.length;
for(int gap = n / 2; gap > 0; gap = gap == 2 ? 1: (int) (gap/2.2))
for(int i = gap; i < n; i++)
{
int temp = list[i];
int j = i;
count++;
for( ; j >= gap && (temp < (list[j - gap])); j -= gap)
{
list[j] = list[j - gap];
count++;
}
list[j] = temp;
}
System.out.println("counter" + count);
}
public static void quickSort(int start, int finish, int list[])
{
int count = 0;
int left = start, right = finish, pivot, temp;
pivot = list[(start + finish) / 2];
do
{
while(list[left] < pivot)
{
left++;
quickcount++;
}
while(pivot < list[right])
{
right--;
quickcount++;
}
if(left <= right)
{
temp = list[left];
list[left++] = list[right];
list[right--] = temp;
quickcount++;
}
} while(left < right);
if(start < right)
quickSort(start, right, list);
if(left < finish)
quickSort(left, finish, list);
}
public static void copy(int list[])
{
for(int i = 0; i < list.length; i++)
X[i] = list[i];
}
public static void restore(int list[])
{
for(int i = 0; i < list.length; i++)
list[i] = X[i];
}
public static void displayArray(int list[])
{
for(int k = 0; k < list.length; k++)
System.out.print(list[k] + " ");
System.out.println();
}
public static void main(String args[])
{
int[] A = new int[100];
for(int i = 0; i < A.length; i++)
A[i] = i + 1;
int[] B = new int[100];
int q = 100;
for(int i = 0; i < B.length; i++)
B[i] = q--;
int[] C = new int[100];
for(int i = 0; i < C.length; i++)
C[i] = (int)(Math.random() * 100 + 1);
displayArray(A);
copy(A);
selectionSort(A);
displayArray(A);
restore(A);
}
Note that QuickSort performance is greatly influenced by your choice of the pivot. With both of your test arrays sorted (ascending / descending) and because you are picking pivot as array[length/2] you are actually always picking the best pivot. So your test case B won't generate maximum number of comparisons for quicksort. If you were picking array[0] as pivot you'd get maximum number of comparisons for test case A and B.
The easiest way to count comparisons is to use a compare function and do it in there.
static int compareCount = 0;
int compareInt(int a, int b) {
compareCount++;
return a - b; // if 0 they are equal, if negative a is smaller, if positive b is smaller
}
Now just use compareInt in all your algorithms and you'll get an accurate count. You'll have to reset compareCount between each run though.