Recursive method in the Java merge sort algorithm - java

I have the below merge sort code in my application. I am very confused on how the recursive method gets called again after it comes out of the if block when the if condition is not met.
I debugged my code, but I am still not getting it. The sort method that calls mergesort(0, number - 1) starts first at mergesort(0, 5). low is less than high, middle is 2, so mergesort(0, 2) is run next. This goes on until we have mergesort(0, 0) in which case low is not less than high,
so it comes out of the if block. But when I debug, the method returns, and it starts again after mergesort(0, 0) case. How does the call happen again?
public class MergeSort {
private int[] numbers;
private int[] helper;
private int number;
public int[] sort(int[] values) {
this.numbers = values;
number = values.length;
this.helper = new int[number];
return mergesort(0, number - 1);
}
private int[] mergesort(int low, int high) {
// check if low is smaller then high, if not then the array is sorted
if (low < high) {
// Get the index of the element which is in the middle
int middle = low + (high - low) / 2;
// Sort the left side of the array
mergesort(low, middle);
// Sort the right side of the array
mergesort(middle + 1, high);
// Combine them both
merge(low, middle, high);
}
return numbers;
}
private int[] merge(int low, int middle, int high) {
// Copy both parts into the helper array
for (int i = low; i <= high; i++) {
helper[i] = numbers[i];
}
int i = low;
int j = middle + 1;
int k = low;
// Copy the smallest values from either the left or the right side back
// to the original array
while (i <= middle && j <= high) {
if (helper[i] <= helper[j]) {
numbers[k] = helper[i];
i++;
} else {
numbers[k] = helper[j];
j++;
}
k++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
numbers[k] = helper[i];
k++;
i++;
}
return numbers;
}
}

This is because the mergesort method calls itself twice. You can print out the stack to see what happens.
For example, when call mergesort(0,1), the method will call mergesort(0,0) first and then mergesort(1,1).

Related

I am trying out to do the "Count Reverse Pairs" and I can't understand why parentesis() are necessary on this line

I know that addition is commutative therefore I want to perform a storing operation with shorthand operation += at line 46 but the answer differs only when I put the parenthesis, also when when I don't put parenthesis I get wrong answer.
The line is in the merge function.
CODE:
public class ProblemA {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<Integer> (Arrays.asList(1, 3, 2, 3, 1));
int n = arr.size();
//calling mergesort
int total = countPair(arr, n);
System.out.println(total);
}
public static int countPair(ArrayList<Integer> arr, int n) {
int total = mergeSort(arr, 0, n - 1);
return total;
}
public static int mergeSort(ArrayList<Integer> arr, int low, int high) {
//termination condition
if (low >= high) return 0;
//firstly we'll disassociate the elements of array on elementary level
int mid = (low + high) / 2;
//we'll store our count value inside the counter variable
int inv = mergeSort(arr, low, mid);
inv += mergeSort(arr, mid + 1, high);
inv += merge(arr, low, mid, high);
return inv;
}
public static int merge(ArrayList<Integer> arr, int low, int mid, int high) {
//AIM: make a double loop and traverse through the elements and increase the right side pointer
//whenever condition (arr[i]>2*arr[j] is satisfied)
//int i = 0;
int total = 0;
int j = mid + 1;
for (int i = low; i<= mid; i++) {
//looping till we run out the right hand side or condition is not satisfied
while (j<= high && arr.get(i) > 2 * arr.get(j)) {
j++;
} **
* total += (j - (mid + 1)); ** * //parenthesis error here
}
//Now we can move to merging part
ArrayList<Integer> temp = new ArrayList<Integer> ();
int left = low, right = mid + 1;
while (left<= mid && right<= high) {
if (arr.get(left)<= arr.get(right)) {
temp.add(arr.get(left++));
} else {
temp.add(arr.get(right++));
}
}
//for the last right or left remaining element
while (left<= mid) {
temp.add(arr.get(left++));
}
while (right<= high) {
temp.add(arr.get(right++));
}
//now we can copy the remaining elements from temp list to arr
for (int i = low, k = 0; i<= high; i++) {
arr.set(i, temp.get(k++));
}
return total;
}
}
OUTPUT(with parenthesis) "total += (j-(mid+1))":
2
OUTPUT(without parenthesis) "total += j-mid+1":
16
This is happening because if you use parenthesis, java will decide which expression you want to evaluate first. If you don't use parenthesis then everything goes executed from left to right.
Example:
int total=1;
int total1=1;
int total2=1;
total+=total1-total2+1;
System.out.println(total);
Output : 2
The reason is here, expression got evaluated as total=total+total1-total2+1;
i.e. total=1+1-1+1=2;
For parenthesis based expression, it gets evaluated first & then others:-
int total=1;
int total1=1;
int total2=1;
total+=(total1-(total2+1));
System.out.println(total);
Output : 0 , evaluation will happen like below :-
total=total+(1-(1+1));
total=1+(1-(2));
total=1+(-1);
total=0;
Reference : https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html

How to find the recurrence relation for the basic operation of algorithm

This an algorithm to count inversions in an array, a divide and conquer algorithm. I want to know what is the basic operation of the this algorithm and how do I set up a recurrence relation to analyze the number of executions of the basic step?
The definition of basic step: The operation contributing the most to the total running time of an algorithm. It is typically the most time consuming operation in the algorithm’s innermost loop.
Examples: Key comparison operation; arithmetic operation (division being the most time-consuming, followed by multiplication).
This is the algorithm
public static int merge(int[] arr, int[] aux, int low, int mid, int high)
{
int k = low, i = low, j = mid + 1;
int inversionCount = 0;
// while there are elements in the left and right runs
while (i <= mid && j <= high)
{
if (arr[i] <= arr[j]) {
aux[k++] = arr[i++];
}
else {
aux[k++] = arr[j++];
inversionCount += (mid - i + 1); // NOTE
}
}
// copy remaining elements
while (i <= mid) {
aux[k++] = arr[i++];
}
// no need to copy the second half
// copy back to the original array to reflect sorted order
for (i = low; i <= high; i++) {
arr[i] = aux[i];
}
return inversionCount;
}
// Sort array `arr[low…high]` using auxiliary array `aux`
public static int mergeSort(int[] arr, int[] aux, int low, int high)
{
// Base case
if (high == low) { // if run size == 1
return 0;
}
// find midpoint
int mid = (low + ((high - low) >> 1));
int inversionCount = 0;
// recursively split runs into two halves until run size == 1,
// then merges them and return up the call chain
// split/merge left half
inversionCount += mergeSort(arr, aux, low, mid);
// split/merge right half
inversionCount += mergeSort(arr, aux, mid + 1, high);
// merge the two half runs
inversionCount += merge(arr, aux, low, mid, high);
return inversionCount;
}
How do I set up a recurrence relation to analyze number of executions of the basic steps? Please also provide explanation.

Why this merge procedure is slightly slower?

Recently I was testing two variations of the merge method in Mergesort and one turns our to be slightly faster than the other. For a large enough input (say, an array of 10-100 million or more randomly ordered elements), one merge method takes around 100ms longer than the other.
Here's the one taking more time:
private static void merge(int[] a, int low, int mid, int hi) {
int temp[] = new int[(hi - low) + 1];
int cLeft = low;
int cRight = mid + 1;
int cTemp = 0;
while (cLeft <= mid && cRight <= hi) {
if (a[cLeft] <= a[cRight]) {
temp[cTemp++] = a[cLeft++];
} else {
temp[cTemp++] = a[cRight++];
}
}
//copy the remaining left elements to the right end
System.arraycopy(a, cLeft, a, low + cTemp, mid - cLeft + 1);
//copy temp to a
System.arraycopy(temp, 0, a, low, cTemp);
}
...and this is the faster one
private static void merge(int[] list, int lowIndex, int midIndex, int highIndex) {
int[] L = new int[midIndex - lowIndex + 2];
for (int i = lowIndex; i <= midIndex; i++) {
L[i - lowIndex] = list[i];
}
L[midIndex - lowIndex + 1] = Integer.MAX_VALUE;
int[] R = new int[highIndex - midIndex + 1];
for (int i = midIndex + 1; i <= highIndex; i++) {
R[i - midIndex - 1] = list[i];
}
R[highIndex - midIndex] = Integer.MAX_VALUE;
int i = 0, j = 0;
for (int k = lowIndex; k <= highIndex; k++) {
if (L[i] <= R[j]) {
list[k] = L[i];
i++;
} else {
list[k] = R[j];
j++;
}
}
}
Both variations of MergeSort are given different arrays of same length with same elements at identical positions as their input. In other words, input of one algorithm is a copy of input of the other.
Although the difference in running time is negligible (the average running time doesn't change, i.e. remains 100ms, no matter how much we increase the size after 1 million mark.), I am eager to know what makes the faster merge faster. For me, the former method is cleaner and easier to implement. However, if the other one remains faster, I probably will switch to that.

Need assistance JAVA quicksort recursion

For school I have to write a quicksort for an Array of strings. I think my partition is good but I keep getting error with the recursion.
public static int partition(String[] input, int max) {
// pivot is dividing point
// I keeps track of lesser values
// count is just a counter
// Pivots in place
int pivot = 0;
int i = 1;
int count = 1;
while (count <= max) {
if (input[count].compareTo(input[pivot]) < 0) {
input[i] = input[count];
i = i + 1;
}
if (count == max) {
input[i] = input[pivot];
input[pivot] = input[i];
}
count++;
}
return pivot;
}
public static void qSort(String[] input) {
int index = partition(input, input.length - 1);
int count = 0;
if (count < index - 1) {
partition(input, index - 1);
}
if (index + 1 < count && count < input.length) {
partition(input, input.length - 1);
}
count++;
}
Your implementation has a lot of mistakes. First of all, you're always choosing the pivot to be the element at index 0 of the array. The idea of quicksort is that you choose some pivot, partition the array around it, then recursively apply quicksort on both partitions around the chosen pivot. How do you identify the start and end of the partitions you'll call quicksort recursively on? You need to pass those as arguments to your partition method as others have mentioned in the comments, same thing for the quicksort method itself.
public void swap (String [] array, int i, int j) {
String tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static int partition (String [] array, int low, int high) {
String pivot = array[low];
int index = low+1;
for (int i = low+1; i <= high; i++) {
if (pivot.compareTo(array[i]) > 0) {
swap(array, i, index);
index++;
}
}
swap(array, low, index-1);
return index-1;
}
public static void qsort(String [] array, int low, int high) {
if (low < high) {
int p = partition(array, low, high);
qsort(array, low, p);
qsort(array, p+1, high);
}
}
Obviously this isn't the best way to choose the pivot as some arrays will make the algorithm perform very poorly. A better method would be to use a random pivot each time.

Counting Operations of Merge Sort and Selection Sort

I want to compare count of operations of the sorting algorithms Merge Sort and Selection Sort, but I have some problems figuring out which operations to count and which not..
Here are my implementations. I think I'm counting the operations of Selection Sort in the right way, but I don't know about Merge Sort:
Selection Sort:
public class SelectionSort {
private int exchanges, comparisons;
public void selectionSort(int[] x) {
for (int i=0; i<x.length-1; i++) {
for (int j=i+1; j<x.length; j++) {
if (x[i] > x[j])
{
//... Exchange elements
int temp = x[i];
x[i] = x[j];
x[j] = temp;
exchanges++;
}
comparisons++;
}
}
System.out.println("SelSort_Exchanges: "+exchanges);
System.out.println("SelSort_Comparisons: "+comparisons);
System.out.println("SelSort_Operations: "+(exchanges+comparisons));
}
}
If I put in an array of 10 int I get:
SelSort_Comparisons: 45
SelSort_Exchanges: 27
SelSort_Operations: 72
Seems right to me, but now for Merge Sort:
public class Mergesort {
private int[] numbers;
private int[] helper;
private int number;
private int comparisons, exchanges;
public void sort(int[] values) {
this.numbers = values;
number = values.length;
this.helper = new int[number];
mergesort(0, number - 1);
System.out.println("MerSort_Comparisons "+comparisons);
System.out.println("MerSort_Exchanges "+exchanges);
System.out.println("MerSort_Operations "+(comparisons+exchanges));
System.out.println();
}
private void mergesort(int low, int high) {
// Check if low is smaller then high, if not then the array is sorted
if (low < high)
{
// Get the index of the element which is in the middle
int middle = (low + high) / 2;
// Sort the left side of the array
mergesort(low, middle);
// Sort the right side of the array
mergesort(middle + 1, high);
// Combine them both
merge(low, middle, high);
}
}
private void merge(int low, int middle, int high) {
// Copy both parts into the helper array
for (int i = low; i <= high; i++) {
helper[i] = numbers[i];
exchanges++;
}
int i = low;
int j = middle + 1;
int k = low;
// Copy the smallest values from either the left or the right side back
// to the original array
while (i <= middle && j <= high) {
if (helper[i] <= helper[j]) {
numbers[k] = helper[i];
i++;
exchanges++;
} else {
numbers[k] = helper[j];
j++;
exchanges++;
}
k++;
comparisons++;
}
// Copy the rest of the left side of the array into the target array
while (i <= middle) {
numbers[k] = helper[i];
exchanges++;
k++;
i++;
}
}
}
now I get
MerSort_Comparisons 22
MerSort_Exchanges 61
MerSort_Operations 83
as a result but I don't know if its right. Result of Comparisons seems right to me, but if I take an array of 20 for example it doesnt seem right anymore.
Could anyone help me with this and tell me where exactly I have to put my comparison and exchange increments?
thanks in advance! :)
Simplest way would be to create two methods, Compare and Swap, and increase counter inside of them. Whatever sorting algorithm you implement, it should only interact with the array using these methods.
Also, this page can help you analyze different sorting algorithms visually.

Categories