了解合并排序代码

时间:2017-02-03 19:27:46

标签: c sorting merge

我正在尝试使用以下网站作为资源实施合并排序。

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

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

对我来说,我的代码看起来很好;但是,鉴于我的输出不正确,显然是错误的。我不确定它出错的地方或原因。当我查看它的图像解释时,我想我理解合并排序但是试图实现它非常困难。我试图给所有变量名称一些意义,以帮助减少混淆(“我”,“j”,“k”,“m和”n“有点难以跟踪它们代表什么)。我也有尝试使用print语句帮助调试我的代码,但递归对我来说从未如此简单,所以我不确定我的打印语句告诉我除输出是什么之外的确切内容。我也尝试使用调试器但是无论什么原因,调试器都没有列出我的任何数组中的值,所以我无法通过调试器继续进行。任何帮助澄清合并排序的实现都将非常感激。

#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");
}

输出如下:

  

未排序的数组!

     

14 7 3 12 9 11 6 2

     

14 7 ===左阵列

     

3 12 ===右阵列

     

14 7 ====排序后离开数组

     

3 12 ====排序后的正确数组

     

3 12 14 7 =====合并后=====

     

3 12 ===左数组

     

14 7 ===右阵列

     

3 12 ====排序后离开数组

     

14 7 ====排序后的正确数组

     

3 12 14 7 9 11 6 2 =====合并后=====

     

3 12 14 7 ===左阵列

     

9 11 6 2 ===右阵列

     

3 12 14 7 ====排序后的左数组

     

9 11 6 2 ====排序后的正确数组

     

3 9 11 6 2 12 14 7 =====合并后=====

     

排序数组!

     

3 9 11 6 2 12 14 7

更新输出:

  

未排序的数组!
  14 7 3 12 9 11 6 2
  14 ===左阵列
  7 ===右阵列
  7 14 =====合并后=====
  7 ===左阵列
  14 ===右阵列
  7 14 3 12 =====合并后=====
  7 14 ===左阵列
  3 12 ===右阵列
  3 7 12 14 =====合并后=====
  3 ===左阵列
  7 ===右阵列
  3 7 12 14 9 11 =====合并后=====
  3 ===左阵列
  7 ===右阵列
  3 7 12 14 9 11 6 2 =====合并后=====
  3 7 ===左阵列
  12 14 ===右阵列
  3 7 12 14 9 11 6 2 =====合并后=====
  3 7 12 14 ===左阵列
  9 11 6 2 ===右阵列
  3 7 9 11 6 2 12 14 =====合并后=====
  排序数组!
  3 7 9 11 6 2 12 14

3 个答案:

答案 0 :(得分:1)

您的错误在这里:

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

你需要通过左数组缩进

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

顺便说一下,您使用

评论过的部分
  

//不知道这是什么

是否在程序已经完成插入两个数组之一的所有索引的情况下继续插入索引。

更新: 还有另一个(更大的)问题。您还需要跟踪原始阵列的当前位置。如果仔细观察,即使使用右半部分,也会看到始终从numbers[0]开始复制值。 但是保留另一个计数器是一种混乱的方法,所以考虑将函数定义更改为:

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

这将有助于保持清洁和简单。 这应该是它的样子:

#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++;
    }
}

答案 1 :(得分:0)

merge初始化临时左右数组时,需要修复您正在访问的索引。例如,您目前有

// 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");

但这假设array是原始数组的一部分。虽然视觉上mergesort算法从字面上将原始数组拆分为子数组,但代码实际上并没有这样做。您仍在篡改原始数组,但参数int startIndex, int midIndex, int endIndex就像原始数组中的边框一样。因此,在初始化leftArrayrightArray时,您需要使用类似的参数访问array的元素,

// 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");

答案 2 :(得分:0)

此版本正常运行。这是原始代码,在评论中注明了修复//修复此行//。其他更改是由于使用Microsoft编译器(旧C语法)。还清理了缩进。

#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");
}