简体   繁体   中英

Arrays and mergeSort in c++?

void Merge(int *array, int lo, int mid, int hi) {
    int tArray[20];
    int loBeg = lo;
    int count = lo;
    int hiBeg = mid + 1;
    while (loBeg <= mid && hiBeg <= hi) {
        if (array[loBeg] < array[hiBeg]) {
            tArray[count] = array[loBeg];
            loBeg++;
            count++;
        } else {
            tArray[count] = array[hiBeg];
            hiBeg++;
            count++;
        }
    }
    while (loBeg <= mid) {
        tArray[count++] = array[loBeg++];
    }
    while (hiBeg <= hi) {
        tArray[count++] = array[hiBeg++];
    }
    for (int i = 0; i < count; i++) {
        array[i] = tArray[i];
    }
}

void mergeSort(int *array, int lo, int hi) {
    if (lo < hi) {
        int mid = (lo + hi) / 2;
        mergeSort(array, lo, mid);
        mergeSort(array, mid + 1, hi);
        Merge(array, lo, mid, hi);
    }
}

int main(int argc, const char * argv[]) {
    int array[] = {90, 99, 63, 82, 93, 76, 81, 76};
    //int temp[8];
    mergeSort(array, 0, 7);
    for (int i = 0; i < 8; i++) {
        std::cout << array[i] << std::endl;
    }
    return 0;
}

My Question is about the nature of arrays during this merge sort code. This code only works if in Merge, O set tArray[20]; to have that initial value of 20. Why can I not set the initial value to be hi + 1 which in this case is 8 (same as array)? However, if i uncomment out the temp[8] array, and pass that through both mergeSort and Merge , and use that as tArray in Merge (with an initial size of 8 ), then it works.

I think my lack of understanding is also the reason why my original merge() (see below) function does not work either:

    //this was my original merge code which does not work.
    void merge(int *array, int lo, int mid, int hi) {
        int tempArray[hi + 1];
        int loBeg = lo;
        int hiBeg = mid + 1;
        for (int i = 0; i <= hi; i++) {
            if (hiBeg > hi || (array[loBeg] < array[hiBeg] && loBeg <= mid)) {
                tempArray[i] = array[loBeg++];
            } else {
                tempArray[i] = array[hiBeg++];
            }
        }
        for (int i = 0; i <= hi; i++) {
            array[i] = tempArray[i];
        }
    }

So Basically I am wondering why in the first Merge() function, I have to set the tArray initial size to be 20 instead of the same size as array, unless I pass along a temp array from main (which is initialized to be the same size as array) and then furthermore, why my original merge() function does not work, but I think it has to do with my lack of understanding of the first Merge() function.

When you create an array like this int array[20]; you are allocating it in the stack memory of your program. This memory is allocated before the program starts.

Here comes your problem. When you try to do int array[hi + 1]; you are asking it to allocate amount of memory that is not known before the program starts, and it results in an error.

What you need to do in this case, is use dynamic memory. This memory is allocated and freed on the run. Meaning that you can do int* array = new int[hi + 1]; and it won't result in an error.

The whole merge function would be:

void merge(int *array, int lo, int mid, int hi) {
    int* tempArray = new int[hi + 1];
    int loBeg = lo;
    int hiBeg = mid + 1;
    for (int i = 0; i <= hi; i++) {
        if (hiBeg > hi || (array[loBeg] < array[hiBeg] && loBeg <= mid)) {
            tempArray[i] = array[loBeg++];
        } else {
            tempArray[i] = array[hiBeg++];
        }
    }
    for (int i = 0; i <= hi; i++) {
        array[i] = tempArray[i];
    }

    delete[] tempArray;
}

I wouldn't advice you to manage the dynamic memory yourself. You should use STL for that: vector<int> tempArray(hi + 1); instead of int* tempArray = new int[hi + 1]; . This way you don't have to have the delete[] tempArray; at the end.

Merge sort is thinked to work with lists because when you merge 2 lists you can freely attach each element to the element you want.

Example:

Merge(List{3,4},List{5,6});

results in the following operation

List Merge(List a, List b){ //2 instructions even when Lists are long 1000000 elements
    List newlist;
    newlist.head = a.head;
    newlist.tail = b.tail;
    a.tail.next = b.head;
    b.head.prev = a.tail;
    return newList;
}

when you have arrays you have actually to allocate a new array of a new size:

int * Merge( int array* a, int array* b, unsigned int sizea, unsigned int sizeb){
    //int tArray[20]; // :/ wrong
    int * newarray = new int[sizea+sizeb];

    for(unsigned int i=0; i<sizea; i++)
        newarray[i]=a[i];

    for(unsigned int i=0; i<sizeb; i++)
        newarray[i+sizea]=a[i];

    delete []a;
    delete []b;
    return newarray;
}

This incidentally make the alogrithm much more expensive. Even in the case you have pre-allocated a big enough array the cost of the algorithm is greater because you have to copy all elements at each merge.

There are multiple issues in your code, here are a few of them:

void Merge(int *array, int lo, int mid, int hi) {
    // The size should be hi - lo + 1, not hi + 1
    int tArray[hi + 1];
    int loBeg = lo;
    // count should start at 0, not lo
    int count = lo;
    int hiBeg = mid + 1;
    while (loBeg <= mid && hiBeg <= hi) {
        if (array[loBeg] < array[hiBeg]) {
            tArray[count] = array[loBeg];
            loBeg++;
            count++;
        } else {
            tArray[count] = array[hiBeg];
            hiBeg++;
            count++;
        }
    }
    while (loBeg <= mid) {
        tArray[count++] = array[loBeg++];
    }
    while (hiBeg <= hi) {
        tArray[count++] = array[hiBeg++];
    }
    for (int i = 0; i < count; i++) {
        // it should be array[lo + i], not array[i]
        array[i] = tArray[i];
    }
}

Here are some explanations for the 3 mistakes:

  1. You don't want an array of size hi + 1 each time, when you go down the "merging tree", you have to merge array of lower size, so you need to use hi - lo + 1 . Actually, using hi + 1 should not be an issue in this case, but you are allocating more memory than you really need.

  2. You should not start at lo but rather at 0 , or your tArray[count] will be out-of-bound. Start at lo worked in your case because you were allocating a very big array (of size 20 or hi + 1 ), but it won't work with an array of size hi - lo + 1 , so you should be careful.

  3. Maybe the biggest mistake - You were always replacing the first count cell of your original array... No! You want to replace the cell between lo and hi , not between 0 and count .

You need to remember that when you call Merge (array, lo, mid, hi) , you want to merge the array[lo:mid] and array[mid+1:hi] into array[lo:hi] .


Here are some detailed explanation:

Let's start with the following array [90, 99, 63, 82, 93, 76, 81, 76] , at each call to mergeSort , you are dividing the array in 2 equal parts:

First time you get [90, 99, 63, 82] ( lo = 0 , hi = 3 ), and [93, 76, 81, 76] ( lo = 4 and hi = 7 ), and you keep dividing until you have 8 arrays of size 1 . I will write (array, lo, hi) , eg ([90], 0, 0) for simplicity.

You should get to a point where you have ([90], 0, 0) , ([99], 1, 1) , and so on... You will merge these 2 by 2, so you are (for instance) going to merge ([93], 4, 4) and ([76], 5, 5) . When you merge these 2 arrays, you get a call of Merge like the following:

Merge (array, 4, 4, 5) // mid is (4 + 5) / 2 = 4

So, what happens in the Merge function?

  1. You should have noticed that since you are merging two arrays of size 1 ( [93] and [76] ), you need a temporary array of size 2. If you use tArray[hi + 1] , you allocate an array of size hi + 1 = 6 , which is a lot bigger than what you actually need (may not make a big difference here, but imagine if you have an original array of size one billion!). So allocating an array of size hi - lo + 1 = 5 - 4 + 1 = 2 is sufficient.

  2. You are actually is the range 4 to 5 of your initial array, so you want to work on that part, you don't want to erase the part 0 to 1 that you have already sorted! But what happens if you do array[i] = tArray[i] in your loop? Well, i starts at 0 and goes to count (which should be 2 in this case), so you are replacing array[0] and array[1] instead of array[4] and array[5] . This can be fixed by doing array[lo + i] instead of array[i] .

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