简体   繁体   中英

Understanding merge sort code

I'm trying to implement a merge sort using the following websites as resources.

http://interactivepython.org/courselib/static/pythonds/SortSearch/TheMergeSort.html

https://www.khanacademy.org/computing/computer-science/algorithms/merge-sort/p/challenge-implement-merge

To me, my code looks fine; however, given my incorrect output it is obviously wrong. I'm not sure where or why it is wrong. I think I understand merge sort when I look at the image explanations of it but trying to implement it has been very difficult. I have tried to give all variables names some meaning to help reduce confusion ("i", "j", "k", "m, and "n" are a little hard to keep track of what they represent). I have also tried to use print statements to help debug my code but recursion has never been easy for me so I'm not sure exactly what my print statements are telling me other than the output is wrong. I've also tried to use the debugger but for whatever reason the debugger doesn't list the values that are in any of my arrays so I can't really proceed via the debugger. Any help in clarifying the implementation of merge sort would be greatly appreciated.

#include <stdio.h>

void merge_sort(int array[], int startIndex, int endIndex);
void merge(int array[], int startIndex, int midIndex, int endIndex);

int main(void)
{
    int size = 8;
    int numbers[8] = {14, 7, 3, 12, 9, 11, 6, 2};

    printf("Unsorted Array!\n");
    for(int i = 0; i < size; i++)
    {
        printf("%i ", numbers[i]);
    }
    printf("\n");

    merge_sort(numbers, 0, 7);

    printf("Sorted Array!\n");
    for(int i = 0; i < size; i++)
    {
        printf("%i ", numbers[i]);
    }
    printf("\n");
}

void merge_sort(int array[], int startIndex, int endIndex)
{
    // determine size of the array
    int size = (endIndex - startIndex) + 1; // updated

    // if the array has more than 1 element then it needs broken down into smaller arrays
    if(size > 1)
    {
        int midIndex = (startIndex + endIndex) / 2;

        merge_sort(array, startIndex, midIndex); // Sort the LEFT side
        merge_sort(array, midIndex + 1, endIndex); // Sort the RIGHT side

        merge(array, startIndex, midIndex, endIndex);
    }
}

void merge(int array[], int startIndex, int midIndex, int endIndex)
{
   int leftSize = midIndex - startIndex + 1;
   int rightSize = endIndex - midIndex;
   int originalArrayIndex = 0;
   int leftArray[leftSize];
   int rightArray[rightSize];
   int currentLeftIndex = 0;
   int currentRightIndex = 0;

   // fill the leftArray
   for(int i = 0; i < leftSize; i++)
   {
       leftArray[i] = array[i];
       printf("%i ", leftArray[i]);
   }
   printf(" === Left array\n");

   // fill the rightArray
   for(int i = 0; i < rightSize; i++)
   {
       rightArray[i] = array[leftSize + i];
       printf("%i ", rightArray[i]);
   }
   printf(" === Right array\n");

   // do the actual merge
   // leftStart < leftSize and rightStart < rightSize
   while((currentLeftIndex < leftSize) && (currentRightIndex < rightSize))
   {
       // if the left array value is smaller than the right array value then that means the left array value goes into the original array[]
       if(leftArray[currentLeftIndex] < rightArray[currentRightIndex])
       {
           array[originalArrayIndex] = leftArray[currentLeftIndex];
           originalArrayIndex++;
           currentLeftIndex++;
       }
       else
       {
           array[originalArrayIndex] = rightArray[currentRightIndex];
           originalArrayIndex++;
           currentRightIndex++;
       }
   }

   // no clue what this is
   while(currentLeftIndex < leftSize)
   {
       array[originalArrayIndex] = leftArray[currentLeftIndex];
       originalArrayIndex++;
       currentLeftIndex++;
   }

   // no clue what this is
   while(currentRightIndex < rightSize)
   {
       array[originalArrayIndex] = rightArray[currentRightIndex];
       originalArrayIndex++;
       currentRightIndex++;
   }

   for(int i = 0; i < leftSize; i++)
   {
       printf("%i ", leftArray[i]);
   }
   printf(" ==== left array after sort\n");

   for(int i = 0; i < rightSize; i++)
   {
       printf("%i ", rightArray[i]);
   }
   printf(" ==== right array after sort\n");

   for(int i = 0; i < endIndex + 1; i++)
   {
       printf("%i ", array[i]);
   }
   printf(" ===== post merge =====\n");
}

Output is listed below:

Unsorted Array!

14 7 3 12 9 11 6 2

14 7 === Left array

3 12 === Right array

14 7 ==== left array after sort

3 12 ==== right array after sort

3 12 14 7 ===== post merge =====

3 12 === Left array

14 7 === Right array

3 12 ==== left array after sort

14 7 ==== right array after sort

3 12 14 7 9 11 6 2 ===== post merge =====

3 12 14 7 === Left array

9 11 6 2 === Right array

3 12 14 7 ==== left array after sort

9 11 6 2 ==== right array after sort

3 9 11 6 2 12 14 7 ===== post merge =====

Sorted Array!

3 9 11 6 2 12 14 7

Updated Output:

Unsorted Array!
14 7 3 12 9 11 6 2
14 === Left array
7 === Right array
7 14 ===== post merge =====
7 === Left array
14 === Right array
7 14 3 12 ===== post merge =====
7 14 === Left array
3 12 === Right array
3 7 12 14 ===== post merge =====
3 === Left array
7 === Right array
3 7 12 14 9 11 ===== post merge =====
3 === Left array
7 === Right array
3 7 12 14 9 11 6 2 ===== post merge =====
3 7 === Left array
12 14 === Right array
3 7 12 14 9 11 6 2 ===== post merge =====
3 7 12 14 === Left array
9 11 6 2 === Right array
3 7 9 11 6 2 12 14 ===== post merge =====
Sorted Array!
3 7 9 11 6 2 12 14

Your error is here:

   // fill the rightArray
   for(int i = 0; i < rightSize; i++)
   {
       rightArray[i] = array[rightSize + i];
       printf("%i ", rightArray[i]);
   }
   printf(" === Right array\n");

you need to indent by left array size :

   rightArray[i] = array[leftSize + i];

By the way, the parts you commented on with

// no clue what this is

are there to keep inserting indices in the case where the program already finished inserting all of the indices of one of the two arrays.

Update: There is another (bigger) problem. You also need to keep track of your current position at the original array. If you look closely you'll see that you always copying values starting from numbers[0] even when working with its right half. But keeping another counter is a messy approach so consider changing your function definitions to:

merge_sort (int array[], int arraySize);
merge (int leftArray[], int leftSize, int rightArray[], int rightSize, int targetArray[]);

This will help keep things clean and simple. This is what it should look like:

#include <stdio.h>

void merge_sort(int array[], int arraySize);
void merge(int leftArray[], int leftSize, int rightArray[], int rightSize, int targetArray[]);

int main(void)
{
    int numbers[8] = { 14, 7, 3, 12, 9, 11, 6, 2 };
    int size = sizeof(numbers) / sizeof(int);

    printf("Unsorted Array!\n");
    for (int i = 0; i < size; i++)
    {
        printf("%i, ", numbers[i]);
    }
    printf("\n");

    merge_sort(numbers, size);

    printf("Sorted Array!\n");
    for (int i = 0; i < size; i++)
    {
        printf("%i ", numbers[i]);
    }
    printf("\n");
}

void merge_sort(int array[], int arraySize)
{
    if (arraySize > 1)
    {
        int leftSize = arraySize / 2;
        int rightSize = arraySize - leftSize;

        merge_sort(array, leftSize); // Sort the LEFT side
        merge_sort(array + leftSize, rightSize); // Sort the RIGHT side

        int* targetArray = (int*)malloc(arraySize * sizeof(int));
        merge(array, leftSize, array + leftSize, rightSize, targetArray);
        memcpy(array, targetArray, arraySize * sizeof(int));

        free(targetArray);
    }
}

void merge(int leftArray[], int leftSize, int rightArray[], int rightSize, int targetArray[])
{
    int currentLeftIndex = 0;
    int currentRightIndex = 0;
    int targetIndex = 0;

    while ((currentLeftIndex < leftSize) && (currentRightIndex < rightSize))
    {
        if (leftArray[currentLeftIndex] < rightArray[currentRightIndex])
        {
            targetArray[targetIndex] = leftArray[currentLeftIndex];
            currentLeftIndex++;
        }
        else
        {
            targetArray[targetIndex] = rightArray[currentRightIndex];
            currentRightIndex++;
        }
        targetIndex++;
    }

    while (currentLeftIndex < leftSize)
    {
        targetArray[targetIndex] = leftArray[currentLeftIndex];
        targetIndex++;
        currentLeftIndex++;
    }

    while (currentRightIndex < rightSize)
    {
        targetArray[targetIndex] = rightArray[currentRightIndex];
        targetIndex++;
        currentRightIndex++;
    }
}

When initializing your temporary left and right arrays in merge , you need to fix the indices that you're accessing. For example, you currently have

// fill the leftArray
for(int i = 0; i < leftSize; i++)
{
    leftArray[i] = array[i];
    printf("%i ", leftArray[i]);
}
printf(" === Left array\n");

// fill the rightArray
for(int i = 0; i < rightSize; i++)
{
    rightArray[i] = array[rightSize + i];
    printf("%i ", rightArray[i]);
}
printf(" === Right array\n");

but this assuming that array is some chunk of the original array. Although visually the mergesort algorithm literally splits up the original array into subarrays, the code doesn't actually do that. You're still tampering with the original array through out, but the parameters int startIndex, int midIndex, int endIndex are like borders within the original array. Therefore, when initializing leftArray and rightArray you need to access the elements of array using the parameters like so,

// fill the leftArray
for(int i = 0; i < leftSize; i++)
{
    leftArray[i] = array[i];
    printf("%i ", leftArray[startIndex + i]);
}
printf(" === Left array\n");

// fill the rightArray
for(int i = 0; i < rightSize; i++)
{
    rightArray[i] = array[midIndex + i];
    printf("%i ", rightArray[i]);
}
printf(" === Right array\n");

This version working. It's the original code with fixes noted in comments as // fixed this line //. Other changes are due to using Microsoft compiler (old C syntax). Also cleaned up the indentation.

#include <stdio.h>

void merge_sort(int array[], int startIndex, int endIndex);
void merge(int array[], int startIndex, int midIndex, int endIndex);

int main(void)
{
int i;            // my compiler is old style c
int size = 8;
int numbers[8] = {14, 7, 3, 12, 9, 11, 6, 2};

    printf("Unsorted Array!\n");
    for(i = 0; i < size; i++)
    {
        printf("%i ", numbers[i]);
    }
    printf("\n");

    merge_sort(numbers, 0, 7);

    printf("Sorted Array!\n");
    for(i = 0; i < size; i++)
    {
        printf("%i ", numbers[i]);
    }
    printf("\n");
    return 0;       // added this //
}

void merge_sort(int array[], int startIndex, int endIndex)
{
// determine size of the array
int size = (endIndex - startIndex) + 1; // updated
int midIndex;    // my compiler is old style C compiler

// if the array has more than 1 element then it needs broken down into smaller arrays
    if(size > 1)
    {
        midIndex = (startIndex + endIndex) / 2;
        merge_sort(array, startIndex, midIndex); // Sort the LEFT side
        merge_sort(array, midIndex + 1, endIndex); // Sort the RIGHT side
        merge(array, startIndex, midIndex, endIndex);
    }
}

void merge(int array[], int startIndex, int midIndex, int endIndex)
{
int leftSize = midIndex - startIndex + 1;
int rightSize = endIndex - midIndex;
int originalArrayIndex = startIndex;   // fixed this line //
//  my compiler doesn't support variable length arrays
//  so allocating from the stack (neither works on large arrays)
int *leftArray  = _alloca(leftSize*sizeof(int));
int *rightArray = _alloca(rightSize*sizeof(int));
// int leftArray[leftSize];
// int rightArray[rightSize];
int currentLeftIndex = 0;
int currentRightIndex = 0;
int i;    // my compiler is old style c compiler

    // fill the leftArray
    for(i = 0; i < leftSize; i++)
    {
        leftArray[i] = array[startIndex+i];   // fixed this line //
        printf("%i ", leftArray[i]);
    }
    printf(" === Left array\n");

    // fill the rightArray
    for(i = 0; i < rightSize; i++)
    {
        rightArray[i] = array[midIndex + 1 + i];  // fixed this line //
        printf("%i ", rightArray[i]);
    }
    printf(" === Right array\n");

    // do the actual merge
    // leftStart < leftSize and rightStart < rightSize
    while((currentLeftIndex < leftSize) && (currentRightIndex < rightSize))
    {
        // if the left array value is smaller than the right array value then that means the left array value goes into the original array[]
        if(leftArray[currentLeftIndex] < rightArray[currentRightIndex])
        {
            array[originalArrayIndex] = leftArray[currentLeftIndex];
            originalArrayIndex++;
            currentLeftIndex++;
        }
        else
        {
            array[originalArrayIndex] = rightArray[currentRightIndex];
            originalArrayIndex++;
            currentRightIndex++;
        }
    }

    // copy any data remaining in leftArray
    while(currentLeftIndex < leftSize)
    {
        array[originalArrayIndex] = leftArray[currentLeftIndex];
        originalArrayIndex++;
        currentLeftIndex++;
    }

    // copy any data remaining in rightArray
    while(currentRightIndex < rightSize)
    {
        array[originalArrayIndex] = rightArray[currentRightIndex];
        originalArrayIndex++;
        currentRightIndex++;
    }

    for(i = 0; i < leftSize; i++)
    {
        printf("%i ", leftArray[i]);
    }
    printf(" ==== left array after sort\n");

    for(i = 0; i < rightSize; i++)
    {
        printf("%i ", rightArray[i]);
    }
    printf(" ==== right array after sort\n");

    for(i = startIndex; i < endIndex + 1; i++)   // fix
    {
        printf("%i ", array[i]);
    }
    printf(" ===== post merge =====\n");
}

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