简体   繁体   English

了解合并排序代码

[英]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 http://interactivepython.org/courselib/static/pythonds/SortSearch/TheMergeSort.html

https://www.khanacademy.org/computing/computer-science/algorithms/merge-sort/p/challenge-implement-merge 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. 我试图给所有变量名称一些含义,以帮助减少混乱(“ 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");
}

Output is listed below: 输出如下:

Unsorted Array! 未排序的数组!

14 7 3 12 9 11 6 2 14 7 3 12 9 11 6 2

14 7 === Left array 14 7 ===左数组

3 12 === Right array 3 12 ===右数组

14 7 ==== left array after sort 14 7 ====排序后的左数组

3 12 ==== right array after sort 3 12 ====排序后的右数组

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

3 12 === Left array 3 12 ===左数组

14 7 === Right array 14 7 ===右数组

3 12 ==== left array after sort 3 12 ====排序后的左数组

14 7 ==== right array after sort 14 7 ====排序后的右数组

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

3 12 14 7 === Left array 3 12 14 7 ===左数组

9 11 6 2 === Right array 9 11 6 2 ===右数组

3 12 14 7 ==== left array after sort 3 12 14 7 ====排序后的左数组

9 11 6 2 ==== right array after sort 9 11 6 2 ====排序后的右数组

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

Sorted Array! 排序数组!

3 9 11 6 2 12 14 7 3 9 11 6 2 12 14 7

Updated Output: 更新的输出:

Unsorted Array! 未排序的数组!
14 7 3 12 9 11 6 2 14 7 3 12 9 11 6 2
14 === Left array 14 ===左数组
7 === Right array 7 ===右数组
7 14 ===== post merge ===== 7 14 =====合并后=====
7 === Left array 7 ===左数组
14 === Right array 14 ===右数组
7 14 3 12 ===== post merge ===== 7 14 3 12 =====合并后=====
7 14 === Left array 7 14 ===左数组
3 12 === Right array 3 12 ===右数组
3 7 12 14 ===== post merge ===== 3 7 12 14 =====合并后=====
3 === Left array 3 ===左数组
7 === Right array 7 ===右数组
3 7 12 14 9 11 ===== post merge ===== 3 7 12 14 9 11 =====合并后=====
3 === Left array 3 ===左数组
7 === Right array 7 ===右数组
3 7 12 14 9 11 6 2 ===== post merge ===== 3 7 12 14 9 11 6 2 =====合并后=====
3 7 === Left array 3 7 ===左数组
12 14 === Right array 12 14 ===右数组
3 7 12 14 9 11 6 2 ===== post merge ===== 3 7 12 14 9 11 6 2 =====合并后=====
3 7 12 14 === Left array 3 7 12 14 ===左数组
9 11 6 2 === Right array 9 11 6 2 ===右数组
3 7 9 11 6 2 12 14 ===== post merge ===== 3 7 9 11 6 2 12 14 =====合并后=====
Sorted Array! 排序数组!
3 7 9 11 6 2 12 14 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. 如果仔细观察,您会发现即使使用右半部分,也总是复制从numbers[0]开始的值。 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. merge初始化临时左右数组时,您需要修复要访问的索引。 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. 但这假设该array是原始数组的一部分。 Although visually the mergesort algorithm literally splits up the original array into subarrays, the code doesn't actually do that. 尽管从外观上看mergesort算法从字面上将原始数组拆分为子数组,但是代码实际上并没有这样做。 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. 您仍然会篡改原始数组,但是参数int startIndex, int midIndex, int endIndex就像原始数组中的边框。 Therefore, when initializing leftArray and rightArray you need to access the elements of array using the parameters like so, 因此,在初始化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");

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). 其他更改归因于使用Microsoft编译器(旧的C语法)。 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");
}

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

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