简体   繁体   English

坚持合并排序的实现

[英]stuck on implementation of merge sort

I am implementing mergesort referencing the geeks for geeks implementation as a guide but my implementation is not working.我正在实施合并排序,参考极客实施作为指南,但我的实施不起作用。

I have my mergesort function that divides my given array in 2 and calls mergesort on half of the list.我有我的 mergesort 函数,它将我的给定数组分成 2 并在列表的一半上调用 mergesort。

Within mergesort, I use a helper function that merges the sub arrays together.在归并排序中,我使用了一个将子数组合并在一起的辅助函数。

I have included my 2 functions.我已经包含了我的 2 个函数。 Could someone be my second set of eyes, I am have staring at this too long to tell the difference between 1's and l's有人会是我的第二双眼睛吗,我盯着这看太久了,无法分辨 1 和 l 之间的区别

It is running but not sorting correctly.它正在运行但没有正确排序。

void merge(int arr[], int temp[], int l, int m, int r) {
  //TODO: implement merge.
  // check arr
  if (arr == NULL) {
        return;
  }

  int left = m - l + 1;
  int right = r - m;

  // copy array into temp array 
  // first half
  int i = 0;
  for (i = 0; i < left; i++) {
        temp[i] = arr[l + i];
  }
  // second half
  int j = 0;
  for (j = m + 1; i < right; j++) {
        temp[j] = arr[m + l + i];
  }

  // compare from each end inserting the lower into the next location of the real array 
  // beginning index of front sub list
  int front = 0;
  // beginning of back sub list
  int back = left;

  // index within array to insert back in 
  int index = l;
  while ((front < left) && (back < right)) {
        
        if (temp[front] <= temp[back]) {
            // temp front goes in the next array spot
            arr[index] = temp[front];
            // increase temp
            front++;
        } else {
            // back is smaller and is put back in the list first 
            arr[index] = temp[back];
            // increase back 
            back++;
        }
        // increase array index
        index++;
  }

  while (front < left) {

        arr[index] = temp[front];
        front++;
        index++;
  }

  while (back < right) {

        arr[index] = temp[back];
        back++;
        index++;
  }
}

void mergeSort(int array[], int temp[], int l, int r) {

    if (r > l) {

        // find middle point
        int middle = l + (r - l) / 2;

        // call merge on first half
        mergeSort(array, temp, l, middle);

        // call merge on second half
        mergeSort(array, temp, middle + 1, r);

        // merge the halves
        merge(array, temp, l, middle, r);
    }
}

I had some trouble understanding your merge logic.我在理解您的merge逻辑时遇到了一些麻烦。

Too many indexes/limits of the form (eg): m - l + 1表格的索引/限制过多(例如): m - l + 1

So, I simplified things by having two temp pointers: tmp_l and tmp_r and added some length/count variables.因此,我通过使用两个temp指针来简化事情: tmp_ltmp_r并添加了一些长度/计数变量。

This allowed some of the indexes to start with 0.这允许一些索引从 0 开始。

Also, your middle calculation was unusual.此外,您的middle计算不寻常。

And, I changed the arg in the call to merge from middle to middle + 1而且,我将调用中的 arg 更改为从middle mergemiddle + 1


I've refactored the code and added a test suite.我重构了代码并添加了一个测试套件。 I've used cpp conditionals to denote old vs new code:我使用cpp条件来表示旧代码与新代码:

#if 0
// old code
#else
// new code
#endif

Anyway, here's my [working] version:无论如何,这是我的 [working] 版本:

#include <stdio.h>
#include <stdlib.h>

#ifdef DEBUG
#define dbgprt(_fmt...) \
    do { \
        printf(_fmt); \
    } while (0)
#else
#define dbgprt(_fmt...) \
    do { \
    } while (0)
#endif

void
merge(int arr[], int temp[], int l, int m, int r)
{

    if (arr == NULL) {
        return;
    }

    // get number of left elements
    int lcnt = (m - l);

    // get number of right elements
    int rcnt = (r - m) + 1;

    dbgprt("merge: BEGIN l=%d m=%d r=%d lcnt=%d rcnt=%d\n",
        l,m,r,lcnt,rcnt);

    int i = 0;

    int *tmp_l = &temp[0];
    for (i = 0; i < lcnt; i++)
        tmp_l[i] = arr[l + i];

    int *tmp_r = &temp[lcnt];
    for (i = 0; i < rcnt; i++)
        tmp_r[i] = arr[m + i];

    int front = 0;
    int back = 0;

    int index = l;

    while ((front < lcnt) && (back < rcnt)) {
        // temp front goes in the next array spot
        if (tmp_l[front] <= tmp_r[back]) {
            arr[index] = tmp_l[front];
            // increase temp
            front++;
        }

        // back is smaller and is put back in the list first
        else {
            arr[index] = tmp_r[back];
            // increase back
            back++;
        }

        // increase array index
        index++;
    }

    while (front < lcnt) {
        arr[index] = tmp_l[front];
        index++;
        front++;
    }

    while (back < rcnt) {
        arr[index] = tmp_r[back];
        index++;
        back++;
    }
}

void
mergeSort(int array[], int temp[], int l, int r)
{
    int middle;

    if (r > l) {
        // find middle point
#if 0
        middle = l + (r - 1) / 2;
#else
        middle = (l + r) / 2;
#endif

        dbgprt("msort: ENTER l=%d m=%d r=%d\n",l,middle,r);

        // call merge on first half
        mergeSort(array, temp, l, middle);

        // call merge on second half
        mergeSort(array, temp, middle + 1, r);

        // merge the halves
#if 0
        merge(array, temp, l, middle, r);
#else
        merge(array, temp, l, middle + 1, r);
#endif

        dbgprt("msort: EXIT l=%d m=%d r=%d\n",l,middle,r);
    }
}

void
dotest(int tstno)
{

    int count = (rand() % 30) + 1;
    printf("dotest: %d %d\n",tstno,count);

    int *arr = malloc(sizeof(*arr) * count);
    int *tmp = malloc(sizeof(*tmp) * count);

    for (int idx = 0;  idx < count;  ++idx) {
        arr[idx] = count - idx;
        dbgprt(" %d",arr[idx]);
    }
    dbgprt("\n");

    mergeSort(arr,tmp,0,count - 1);

    int err = 0;
    for (int idx = 0;  idx < count;  ++idx) {
        printf(" %d",arr[idx]);
        if (arr[idx] != (idx + 1)) {
            printf("?");
            err = 1;
        }
    }
    printf("\n");

    if (err)
        exit(1);

    free(arr);
    free(tmp);
}

int
main(void)
{

    for (int tstno = 1;  tstno <= 4;  ++tstno)
        dotest(tstno);

    return 0;
}

void
merge_ORIG(int arr[], int temp[], int l, int m, int r)
{

    if (arr == NULL) {
        return;
    }

    int i = 0;

    for (i = 0; i < m - l + 1; i++) {
        temp[i] = arr[l + i];
    }

    for (i = 0; i < r + 1; i++) {
        temp[i] = arr[m + l + i];
    }

    int front = l;
    int back = m + 1;
    int index = l;

    while ((front < m - l + 1) && (back < r - m)) {
        if (temp[front] <= temp[back]) {

            // temp front goes in the next array spot
            arr[index] = temp[front];
            // increase temp
            front++;

        }
        else {
            // back is smaller and is put back in the list first
            arr[index] = temp[back];
            // increase back
            back++;
        }

        // increase array index
        index++;
    }

    while (front < m - l + 1) {
        arr[index] = temp[front];
        index++;
        front++;
    }

    while (back < r - m) {
        arr[index] = temp[back];
        index++;
        back++;
    }
}

There is a problem here:这里有一个问题:

  // second half
  int j = 0;
  for (j = m + 1; i < right; j++) {
        temp[j] = arr[m + l + i];
  }

You should write:你应该写:

    // second half
    for (i = 0; i < right; i++) {
        temp[left + i] = arr[m + 1 + i];
    }

Could someone be my second set of eyes, I am have staring at this too long to tell the difference between 1 's and l 's?有人会是我的第二双眼睛吗,我盯着这个看得太久了,无法分辨1l之间的区别?

This is an excellent point!这是一个很好的观点! The solution is to never use l as a variable name, and to simplify the mergesort implementation to remove the confusing and error prone +1 / -1 adjustments.解决方案是永远不要使用l作为变量名,并简化mergesort实现以消除混淆和容易出错的+1 / -1调整。 For this you just need to use the convention where r is the index of the element after the end of the slice.为此,您只需要使用约定,其中r是切片结束后元素的索引。

Here is a modified version:这是一个修改后的版本:

void merge(int arr[], int temp[], int lo, int mid, int hi) {
    // check arr
    if (arr == NULL) {
          return;
    }

    int left = mid - lo;
    int right = hi - mid;

    // copy array into temp array 
    // first half
    for (int i = 0; i < left; i++) {
        temp[i] = arr[lo + i];
    }
    // second half
    for (int i = 0; i < right; i++) {
        temp[left + i] = arr[mid + i];
    }

    // compare from each end inserting the lower into the next location of the real array 
    // beginning index of front sub list
    int front = 0;
    // beginning of back sub list
    int back = left;

    // index within array to insert back in 
    int index = lo;
    while ((front < left) && (back < right)) {
        if (temp[front] <= temp[back]) {
            // temp front goes in the next array spot
            arr[index++] = temp[front++];
        } else {
            // back is smaller and is put back in the list first 
            arr[index++] = temp[back++];
        }
    }

    while (front < left) {
        arr[index++] = temp[front++];
    }

    while (back < right) {
        arr[index++] = temp[back++];
    }
}

void mergeSort(int array[], int temp[], int lo, int hi) {
    if (hi - lo >= 2) {
        // find middle point
        int mid = lo + (hi - lo) / 2;

        // call merge on first half
        mergeSort(array, temp, lo, mid);

        // call merge on second half
        mergeSort(array, temp, mid, hi);

        // merge the halves
        merge(array, temp, lo, mid, hi);
    }
}

The initial call should give 0 and the length of the array as index arguments.初始调用应该给出0和数组的长度作为索引参数。

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

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