简体   繁体   中英

Mergesort & recursion confusion/code not working

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.

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM