繁体   English   中英

了解合并排序代码

[英]Understanding merge sort code

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

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

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

对我来说,我的代码看起来不错; 但是,鉴于我的输出不正确,这显然是错误的。 我不确定哪里或为什么错了。 我认为在查看图像的合并说明时我了解合并排序,但是尝试实现它非常困难。 我试图给所有变量名称一些含义,以帮助减少混乱(“ i”,“ j”,“ k”,“ m”和“ n”有点难以跟踪它们代表什么)。尝试使用打印语句来帮助调试我的代码,但是递归对我而言从来都不是一件容易的事,因此我不确定输出结果是否正确,我的打印语句告诉我的确切信息。不管调试器为何不列出我的任何数组中的值,所以我都无法真正通过调试器进行处理,因此在澄清合并排序实现方面的任何帮助将不胜感激。

#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

您的错误在这里:

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

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

此版本有效。 这是原始代码,其中的注释中有修正,// //修正了这一行。 其他更改归因于使用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");
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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