Mergesort & recursion confusion/code not working - java

I've been at this for a couple days, reading many pseudocode and watching videos to explain recursion and mergesort. I understand mergesort and somewhat understand recursion -- except for when it applies to arrays as is in my code below. I did some debugging and it appears that my program is not sorting correctly regardless of the out of bounds error. I am very lost and would appreciate any help you can offer!
Questions:
1) what does it mean for a recursion on an array? Does it create a sub array that is held by the original array? -- if that makes sense.
2) why is my code running into a out of bounds error even though I followed a tutorial to the t and also set the k value after every pass. Specifically the issue is being encountered.
Here's the code:
public class Merge {
public static void main(String[] args) {
}
static void mergeSort(int arr[]){
int r = arr.length - 1;
Merge.sort(arr,0,r);
System.out.println(arr);
}
static void sort(int arr[], int p, int r){
if(p<r){
int q = (p+r)/2;
sort(arr,p,q);
sort(arr,q+1,r);
merge(arr,p,q,r);
}
}
static void merge(int arr[], int p, int q, int r){
int n1 = q-p+1;
int n2 = r-q;
int L[] = new int[n1];
int R[] = new int[n2];
for(int i = 0; i< n1; i++){
L[i] = arr[i];
}
for(int j = 0; j< n2; j++){
R[j] = arr[q+1+j];
}
int i = 0, j = 0;
int k = 1;
while(i<n1 && j<n2){
if(L[i]<= R[j]){
arr[k] = L[i];
i++;
}
else{
arr[k] = R[j];
j++;
}
k++;
}
while(i<n1){
arr[k] = L[i];
i++;
k++;
}
Error occurs here --> while(j<n2){
arr[k] = R[j];
k++;
}
}
}
Thank you for the help!
edit: Just wanted to say how greatful I am for the amazing replies on this post, thank you so much for your time.

To be honest I don't think your sentence 'recursion on an array' makes any sense.
Your code has one array arr which gets sorted. Your merge method is supposed to be sorting parts of this array, but every time it is called it has the same whole array object. There are no sub-arrays; it's just up to this method to sort the relevant part of this one array. If this method isn't doing what it's supposed to do, then problems will occur.
Let's take a closer look at the loop where you are getting an error:
while(j<n2){
arr[k] = R[j];
k++;
}
Suppose we get to this loop with j < n2. What happens?
We enter the loop because j < n2, so we copy R[j] to arr[k] and then increment k. We go back to the top of the loop, we find j is still less than n2 because neither variable has changed, so we copy R[j] to arr[k] and increment k again. We got back to the top of the loop, find j is still less than n2 and go round again. And so on, and so on, until eventually k falls off the end of arr and we get an ArrayIndexOutOfBoundsException.
In this part of mergesort we are trying to copy into arr the contents of R that haven't already been merged into arr, but we forgot to increment j. So, to fix this loop, increment j as well as k:
while(j<n2){
arr[k] = R[j];
j++;
k++;
}
Note that the previous loop, the one beginning with while(i<n1), increments i and k. This change now makes the two loops look more similar to one another.
So, we run our code again, and what happens? We still get an ArrayIndexOutOfBoundsException. Clearly we haven't solved the problem yet, but have we made any progress at all if we're just getting the same error?
The intention of the merge method is to merge the subarrays of arr from positions p to q inclusive and from positions q+1 to r inclusive. If the two subarrays are sorted, then after merging the whole subarray of arr from p to r will be sorted.
However, when we write the values back into arr, we start at index 1. Is this correct? Suppose arr has length 2, p = 0, q = 0 and r = 1. We have two elements to sort. Where does the first one get written to, and where does the second?
The answer is the first one gets written to arr[1], and your code throws an exception because it attempts to write the second to arr[2], which does not exist.
You want k to start from the start of the subarray you are sorting. You want k to start from p.
So replace the line
int k = 1;
with
int k = p;
We try again, and now we find the code no longer throws an exception but prints something unintelligible like [I#65fb1cd. Annoyingly, this is how Java prints arrays by default. To fix this, add the line import java.util.Arrays; to your file and replace the line
System.out.println(arr);
with
System.out.println(Arrays.toString(arr));
Your code should now print out a list of numbers when it runs.
However, we now see that our code isn't sorting the array correctly. I asked it to sort the values 8, 1, 4, 9 and it came back with 1, 1, 8, 9. The 1 has been duplicated and the 4 has disappeared.
Recall once again that the intention of the merge method is to sort arr from p to r onwards. Take a careful look at what values are being copied from the array into L and R:
for(int i = 0; i< n1; i++){
L[i] = arr[i];
}
for(int j = 0; j< n2; j++){
R[j] = arr[q+1+j];
}
Notice any difference between these two loops, apart from the fact that one uses j instead of i, n2 instead of n1 and R instead of L?
Note that when you copy into R, you are copying values from position q+1 onwards. These are the values in the second sorted subarray. But when you are copying into L, you are copying values from position 0 onwards. This isn't necessarily where the first sorted subarray begins. That of course starts from p.
Replace the first of these loops with:
for(int i = 0; i< n1; i++){
L[i] = arr[p+i];
}
Finally, we run the code and find that we now have a working mergesort program.

Let's break your question down a bit - specifically, what does recursion mean? You can think of it like a loop - it performs an operation on itself until it reaches a stop condition. Take for example, a for loop
for(int i = 0; i < 2; i++)
will perform the operation until it reaches the case where variable i is no longer less than 2. Likewise, recursively
void methodLoop(int input){
int i = input;
if(i < 2){
methodLoop(i+1);
}
else{
System.out.println("Base case reached! I is no longer less than 2!");
}
}
Performs a similar operation, just with recursion instead!
What does this mean for arrays? It depends. What you've touched upon in your question is a concept called multidimentional arrays - arrays within arrays. These work like normal arrays, it's just an array that contains another array in each one of its indexes - these are instantiated as follows
String[][] multidimensionalarray = new array[4][4]
To visualize such a concept, it might be easier to think of it as a coordinate grid, with the indexes being the coordinate places and the value at that index containing information about that place. For example, assuming the multidimensional array has been filled with data like so, it might look like:
4 a b c d
3 e f g h
2 i j k l
1 m n o p
1 2 3 4
and then the value of multidimensionarray[2][3] would return the string k!

Related

Selection sort: storing value instead of index

I'm studying sorting algorithms, including selection sort, so i decided to write a method and it works fine, but when i checked the book it had 2 variables so i checked it and found that it's using a variable to store the current index and the other as temporary to swap
while mine had only the temporary variable that also stored the initial value in the index as the lowest, then compared it to the other values in the array and swapped if a larger value was found.
Here's my code:
public static void selectionSort(int[] arr){
int lowest;
for(int i = 0; i < arr.length - 1; i++){
lowest = arr[i];
for(int j = i+1; j<arr.length; j++){
if(arr[j]<lowest){
lowest = arr[j];
arr[j] = arr[i];
arr[i] = lowest;
}
}
}
}
and Here's the book's
public static void selectionSort(int[] list){
int min;
int temp;
for(int i = 0; i < list.length - 1; i++) {
min = i;
for(int j = i + 1; j < list.length; j++)
if( list[j] < list[min] )
min = j;
temp = list[i];
list[i] = list[min];
list[min] = temp;
}
}
so I looked on the web and all of them follow the same way as the book, so is my code bad or slower, or it is not considered Selection sort ?
sorry for any english mistakes :P
So, the original code works like this:
Start with the first element of the array
Find the smallest number in the array
Swap the two numbers
Repeat the process Until You reach the end of the list
While Yours does this:
Start with the first element of the array
For each element smaller than the current Swap the two numbers
Replace the lowest number with the swapped
Repeat the process
The result should be the same, but probably You are swapping more numbers than the first one. This probably will make it a little bit slower than the original.
Actually it kinda looks like the insertion sort now, so basically You are swapping the elements with all that are bigger than the one You have.
It looks like you're doing a lot more swaps in the nested for loop.
What happens if you do a sort of [4,3,2,1]? I think you'll have more swap operations than the actual selectionSort.
I'm not sure if your code is bad or slower.
SelectionSort is known to be correct, but it isn't fast either.

Two sum - Doesn't work

Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Consider input [3,2,4] and target is 6. I added (3,0) and (2,1) to the map and when I come to 4 and calculate value as 6 - 4 as 2 and when I check if 2 is a key present in map or not, it does not go in if loop.
I should get output as [1,2] which are the indices for 2 and 4 respectively
public int[] twoSum(int[] nums, int target) {
int len = nums.length;
int[] arr = new int[2];
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0;i < len; i++)
{
int value = nums[i] - target;
if(map.containsKey(value))
{
System.out.println("Hello");
arr[0] = value;
arr[1] = map.get(value);
return arr;
}
else
{
map.put(nums[i],i);
}
}
return null;
}
I don't get where the problem is, please help me out
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice. Consider input [3,2,4] and target is 6. I added (3,0) and (2,1) to the map and when I come to 4 and calculate value as 6 - 4 as 2 and when I check if 2 is a key present in map or not, it does not go in if loop.
Okay, let's take a step back for a second.
You have a list of values, [3,2,4]. You need to know which two will add up 6, well, by looking at it we know that the answer should be [1,2] (values 2 and 4)
The question now is, how do you do that programmatically
The solution is (to be honest), very simple, you need two loops, this allows you to compare each element in the list with every other element in the list
for (int outter = 0; outter < values.length; outter++) {
int outterValue = values[outter];
for (int inner = 0; inner < values.length; inner++) {
if (inner != outter) { // Don't want to compare the same index
int innerValue = values[inner];
if (innerValue + outterValue == targetValue) {
// The outter and inner indices now form the answer
}
}
}
}
While not highly efficient (yes, it would be easy to optimise the inner loop, but given the OP's current attempt, I forewent it), this is VERY simple example of how you might achieve what is actually a very common problem
int value = nums[i] - target;
Your subtraction is backwards, as nums[i] is probably smaller than target. So value is getting set to a negative number. The following would be better:
int value = target - nums[i];
(Fixing this won't fix your whole program, but it explains why you're getting the behavior that you are.)
This code for twoSum might help you. For the inputs of integer array, it will return the indices of the array if the sum of the values = target.
public static int[] twoSum(int[] nums, int target) {
int[] indices = new int[2];
outerloop:
for(int i = 0; i < nums.length; i++){
for(int j = 0; j < nums.length; j++){
if((nums[i]+nums[j]) == target){
indices[0] = i;
indices[1] = j;
break outerloop;
}
}
}
return indices;
}
You can call the function using
int[] num = {1,2,3};
int[] out = twoSum(num,4);
System.out.println(out[0]);
System.out.println(out[1]);
Output:
0
2
You should update the way you compute for the value as follows:
int value = target - nums[i];
You can also check this video if you want to better visualize it. It includes Brute force and Linear approach:

How to shift an entire array one position to the right?

public static void moveRight (int a[], int n){
int aux;
for(int i = 1; i<n; i--){
aux = a[i];
a[i]= a [i];
}
}
I been trying with this code but it doesn't work.
When printing the contents of an array, the left starts with the lowest values. If you move the values down the indexes you could say this is to the left. For moving the values up by index you need to start at the end and work down.
// Move one up
for (int i = a.length - 1; i > 0; i--)
a[i] = a[i-1];
// Move one down
for (int i = 0; i < a.length-1; i++)
a[i] = a[i+1];
Note: This is likely to be inefficent compare with using arraycopy which is designed for bulk array copies.
// Move one up
System.arraycopy(a, 0, a, 1, a.length-1);
// Move one down
System.arraycopy(a, 1, a, 0, a.length-1);
First, the loop variable is supposed to be going up. so change i-- to i++. Then, if I correctly understand what you're trying to do, you need a[i] = a[i-1] in the loop body. instead of a[i] = a[i] (which does nothing). Also, you don't need aux.
It's not clear what, if anything, you want to happen to a[0] when everything moves to the right. That should take place after the loop exits. (If you want the last element to move to the first element position, then I retract my comment about tmp; you'll need to store that last element somewhere so it's available after the array element gets overwritten. But it only needs to be stashed once, before the loop starts.)
One other comment: n must never be more than a.length or the loop will throw an exception. If n is always equal to a.length, then you can dispense with the argument and just use a.length in the method.

How to keep parameters unchanged in recursion?

I am trying to implement Quick Sort algorithm, here's my code:
public class Sort {
int count = 0;
public void Partition(int A[], int l, int h) {
if ((h-l) > 1) {
count += (h-l) - 1;
int pivot = A[l];
int i = l+1;
int temp;
int j;
for (j = l + 1; j < h; j++) {
if (A[j] < pivot) { // SWAP
temp = A[i];
A[i] = A[j];
A[j] = temp;
i++;
}
// else : j++
}
temp = A[i-1];
A[i-1] = A[l];
A[l] = temp;
Partition(A, l, i-1);
Partition(A, i, A.length);
}
}
}
the code does sort the input array, but when i count the number of comparisons, it gives a number, much greater than the original number of the comparisons. I added a break point and moved a step by step into the code, and found that the problem lies in the last two lines:
Partition(A, l, i-1);
Partition(A,i, A,length);
The 'i' sent in the 2nd call, is the i resulting from the 1st call to the function Partition, and not the 'i' from the very first code. for example, the first time the code runs on the following input:
3 8 4 6 10 2 5 7 1
the output is: 1 2 3 4 6 10 9 5 7 8
and i = 3.
then the first call to Partition takes i (where i equals 3) and keeps changing the i's value, when it's finally done, the i's value is different than 3, and another wrong value is sent to the 2nd recursive call.
My question is, is there a way, so that the two calls take the same parameter i, without anyone changing it ?
Try this. Don't try to do the sort in Partition; from there, just return the index i (you have to change the return type to int, of course). Then write a different method to implement your quicksort.
public static void quicksort(int[] n, int left, int right){
if (left<right){
int pivotindex=Partition(n, left, right);
quicksort(n, left, pivotindex-1);
quicksort(n, pivotindex+1, right);}
}
I tested this with the following, and it worked perfectly.
public static void main(String[] args){
int[] n= new int[8];
n[0]=3;
n[6]=2;
n[1]=5;
n[3]=20;
quicksort(n, 0, n.length);
for (int i=0; i<n.length; i++){
System.out.print(n[i]+",");
}
}
Since Java uses pass-by-value, there is no way the first call can change the value of the parameter i, since the called method has no reference to it.
You are probably expecting the second call to Partition to be the next one. However, the first call will in turn call Partition twice more, causing the parameter values of the next execution of Partition to be different than you might expect.
Also, please start your method names with a lower-case letter.

(Java) Heapsort - Implementation not sorting half elements?

I've wrote two different implementations of heapsort today and both have given me the same results:
Object i: 18
Object i: 11
Object i: 10
Object i: 9
Object i: 8
Object i: 3
Object i: 7
Object i: 1
Object i: 4
Now I've checked my code with this page here; and believe one of my implementations is exactly the same as the pseudo-code would suggest, whilst the other is very similar to one of the Java implementations.
I'd like to stress the fact that I've actually wrote two different versions, and checked them against implementations I can find! So I am really stumped at the moment! I've stepped through it with a debugger a few times - but have probably must've something along the way? I even made a debugging function which just looped through the list and outputted the contents using System.out.println() - but this still didn't help a great deal!
The algorithm is working on a List - and I have done nothing to optimise it at this stage; it merely is a bit of an experiment at the moment. I have working implementations of QuickSort, BubbleSort & Insertion Sort - but this one has left me stumped!
My first implementation is below:
public static List<Integer> execSort(List<Integer> s) {
int n = (s.size()-1);
Integer t;
for(int i = n/2; i>0; i--){
s = downheap(s, i, n);
}
while(n >= 1){
t= s.remove(0);
s.add(0, s.remove(n-1));
s.add(n-1, t);
n--;
s = downheap(s, 1, n);
}
return s;
}
public static List<Integer> downheap(List<Integer> s, int i, int n){
Integer t = s.get(i-1);
int j;
while( i <= n/2 ){
j = i*2;
if( (j<n) && (s.get(j-1) < s.get(j)))
j++;
if( t >= s.get(j-1)){
break;
} else {
/* Swap them, without using a third variable
- although with all the get()/set() methods
it would be better to have a third one, doh! */
s.set(i-1, (s.get(i-1) + s.get(j-1)));
s.set(j-1, (s.get(i-1) - s.get(j-1)));
s.set(i-1, (s.get(i-1) - s.get(j-1)));
i=j;
}
}
s.set(i-1, t);
return s;
}
You can also see them on Github as Gists:
- Implementation 1
- Implementation 2
Any ideas as to why some of the elements don't want to sort?! I'm aware that this implementation is going to be sub-optimal, that working on a List<> isn't going to be the best data structure and I should probably look at using primitive data-types as opposed to (ab)using auto-boxing... but that's for another post! I just want a working version before I try and improve it ;)
In the gist (you accidentally linked both to the same), you have a few typos
public static List<Integer> execSort(List<Integer> s) {
int start = (s.size()/2)-1;
int end = s.size()-1;
while( start >= 0){
s = sift(s, start, end);
sift takes the count as last argument, not the last index, so the argument ought to be s.size() (or end+1) instead of end.
public static List<Integer> sift(List<Integer> s, int start, int count){
int root = start;
while( ((root*2)+1) < 2 ){
That must be while(root*2+1 < count), and not < 2.
In the code you have here, you have in part the same problem (caused by an odd indexing strategy, I suspect):
for(int i = n/2; i>0; i--){
s = downheap(s, i, n);
Since you always get(i-1) resp. j-1 in downheap, you need an upper bound of s.size() or n+1 while building the initial heap.
}
while(n >= 1){
This loop should only run while n > 1, or you'll swap the smallest element out of place.
t= s.remove(0);
s.add(0, s.remove(n-1));
s.add(n-1, t);
The old root must go in the last place, that's place n, not n-1, s.add(n,t).
n--;
s = downheap(s, 1, n);
}
In downheap, the final
s.set(i-1, t);
is superfluous, you always swap t, so when that line is reached, the element at i-1 already is t.

Categories

Resources