【问题标题】:stuck on implementation of merge sort坚持合并排序的实现
【发布时间】:2021-07-11 22:21:13
【问题描述】:

我正在实施合并排序,将极客引用作为极客实施的指南,但我的实施不起作用。

我有我的合并排序函数,它将给定的数组分成 2 部分,并在列表的一半上调用合并排序。

在合并排序中,我使用一个辅助函数将子数组合并在一起。

我已经包含了我的 2 个函数。谁能成为我的第二双眼睛,我盯着这个太久了,无法分辨 1 和 l 之间的区别

它正在运行,但排序不正确。

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

【问题讨论】:

  • 至少,在你的 last while 循环中,我想你想要:arr[back] = temp[back]; --> arr[index] = temp[back];
  • 您是否在调试器中运行程序并在运行时对其进行跟踪?如果有,你发现了什么?它首先从哪里开始出错?
  • 这个循环for(i = 0; i &lt; r + 1; i++) { 完全覆盖了前一个循环的结果,如果m 不是零,它复制了太多的数组(并且索引越界)。
  • @TomKarzes - 感谢您指出这一行,我意识到我没有更新最新的代码。它已经过编辑以反映我最近的提交
  • 此时听起来您正在使用的代码与发布的代码完全不同。如果是这样,那么您应该更新您的代码,或者删除问题。

标签: arrays c mergesort


【解决方案1】:

这里有问题:

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

你应该写:

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

有人能成为我的第二双眼睛吗,我盯着这个太久了,无法分辨1l之间的区别?

这是一个很好的观点!解决方案是永远不要使用l 作为变量名,并简化mergesort 的实现以消除混淆和容易出错的+1 / -1 调整。为此,您只需要使用约定,其中r 是切片结束后元素的索引。

这是修改后的版本:

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

初始调用应提供0 和数组长度作为索引参数。

【讨论】:

    【解决方案2】:

    我在理解您的 merge 逻辑时遇到了一些困难。

    表格的索引/限制过多(例如):m - l + 1

    所以,我通过 两个 temp 指针:tmp_ltmp_r 简化了事情,并添加了一些长度/计数变量。

    这允许某些索引以 0 开头。

    另外,您的middle 计算不正常。

    而且,我将调用中的 arg 更改为 mergemiddlemiddle + 1


    我重构了代码并添加了一个测试套件。我使用cpp 条件来表示旧代码和新代码:

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

    无论如何,这是我的 [工作] 版本:

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

    【讨论】:

      猜你喜欢
      • 2013-08-11
      • 1970-01-01
      • 2012-02-12
      • 1970-01-01
      • 2018-12-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多