Как реализовать сортировку слиянием из "Введение в алгоритмы" Cormen and Co

Я изучаю алгоритмы от Cormen и Co. и у меня проблема с реализацией сортировки слияния из их псевдокода. Я составил его по:

$ gcc -Wall -g merge_sort.c

у меня есть проблема, потому что для чисел:

2 4 5 7 1 2 3 6

результат:

1 2 2 3 3 4 5 5 

Я попытался внимательно прочитать псевдо-код, но это мне не помогает. Я хочу знать, что я делаю не так. Ниже приведен мой код:

#include <stdio.h>

#define SIZE 8

void merge(int * array_of_integers, int p, int q, int r){
  int n1 = q - p + 1;
  int n2 = r - q; 
  int i, j, k;
  int left_array[n1 + 1];
  int right_array[n2 + 1];

  for(i = 0; i < n1; i++)
    left_array[i] = array_of_integers[p + i];
  for(j = 0; j < n2; j++)
    right_array[j] = array_of_integers[q + j];

  i = 0;
  j = 0;

  for(k = p; k < r; k++){
    if(left_array[i] <= right_array[j]){
    array_of_integers[k] = left_array[i];
    i++;
    } else {
    array_of_integers[k] = right_array[j];
    j++;
    }   
  }
}

void merge_sort(int * array_of_integers, int p, int r){
  if(p < r){
  int q = (p+r)/2;
  merge_sort(array_of_integers, p, q);
  merge_sort(array_of_integers, q + 1, r);
  merge(array_of_integers, p, q, r);
  }
}


void print_array(int * array_of_integers, int amout_of_integers){
  int i;
  for(i = 0; i < amout_of_integers; i++)
    printf("%d ",array_of_integers[i]);
  puts("");
}

int main(void){
  int dataset[] = {2, 4, 5, 7, 1, 2, 3, 6};

  print_array(dataset, SIZE);
  merge_sort(dataset, 0, SIZE);
  print_array(dataset, SIZE);

  return 0;
}

Edit: (правильное решение)

 void merge(int * array_of_integers, int p, int q, int r){
   int n1 = q - p + 1;
   int n2 = r - q; 
   int i, j, k;
   int left_array[n1+1];
   int right_array[n2+1];

   left_array[n1] = 123456798;
   right_array[n2] = 123456798;

   for(i = 0; i < n1; i++)
     left_array[i] = array_of_integers[p + i];
   for(j = 0; j < n2; j++)
     right_array[j] = array_of_integers[q + j + 1];

   i = 0;
   j = 0;

   for(k = p; k <= r; k++){
     if(left_array[i] <= right_array[j]){
       array_of_integers[k] = left_array[i];
       i++;
     } else {
       array_of_integers[k] = right_array[j];
       j++;
     }
   }
 }

 void merge_sort(int * array_of_integers, int p, int r){
   if(p < r){
     int q = (p+r)/2;
     merge_sort(array_of_integers, p, q);
     merge_sort(array_of_integers, q + 1, r);
     merge(array_of_integers, p, q, r);
   }
 }

2 ответов


в вашем коде есть две проблемы.

One, вам нужно уточнить, что означают параметры, которые вы передаете. Внутри merge_sort, похоже, p-первый элемент для сортировки, а r-последний элемент для сортировки. Но, где merge_sort вызывается, в main он передается 0 и SIZE. Здесь 0-Первый элемент для сортировки, но размер не может быть последним элементом, потому что это (предположительно) количество элементов для сортировки. В вашем примере, вы проходите 8, но последний элемент для сортировки-7. Поэтому решите, хотите ли вы изменить merge_sort так, чтобы r было количеством элементов, или вы хотите изменить main, чтобы передать SIZE-1. Аналогично, в merge p кажется первым элементом для слияния, q-последний элемент первого диапазона (поэтому q+1 является первым из второго), а r-последним элементом второго диапазона. Но когда вы копируете из array_of_integers в right_array, вы копируете из q+j. Когда j равно нулю, это копирует последний элемент первого диапазона, но вам нужен первый элемент второго диапазона. Поэтому вам нужно прояснить использование индексов. (Кроме того, вам нужны только элементы n1 и n2 для left_array и right_array, а не n1+1 и n2+1.) Также проверьте цикл на k,for(k = p; k < r; k++). Каким должно быть условие продолжения в этом цикле?

два, когда вы объединяете left_array и right_array, вы не учитываете тот факт, что массив может быть пустым (потому что все элементы были скопированы из него ранее), поэтому сравнение left_array[i] to right_array[j] не работает, потому что i или j указывает элемент вне left_array или right_array соответственно. Например, если я достиг своего предела (n1), то вы не должны сравнивать. Вместо этого, вы должны просто взять элемент из right_array.


этот работает, хотя он реализован на Java, логика такая же, очевидно . Я позаботился обо всех пунктах, предложенных в ответе Эрика. Пожалуйста, проверьте код, это само собой разумеется.

import java.util.*;
class MergeSort
{

    public static void main(String args[])
    {
        int testArray[] = {1,3,5,3,1,7,8,9};
        mergeSort(testArray,0,testArray.length-1);
        System.out.println(Arrays.toString(testArray));
    }

    protected static void mergeSort(int arr[], int p, int r)
    {
        int q;
        if (p<r)
        {
            q = (p+r)/2;
            mergeSort(arr,p,q);
            mergeSort(arr, q+1, r);
            merge(arr,p,q,r);   
        }   
    }

    protected static void merge(int arr[], int p, int q, int r)
    {    
        int n = q-p+1;
        int m = r-q;

        int L[] = new int[n+1];
        int R[] = new int[m+1];
        int i,j,k;

        for(i=0; i< n; i++)
        {
            L[i] = arr[p+i];    
        }
        for(j=0; j< m; j++)
        {
            R[j] = arr[q+j+1];    
        }

        L[n] = Integer.MAX_VALUE;
        R[m] = Integer.MAX_VALUE;

        i = 0;
        j = 0;
        for(k = p; k<= r; k++)
        {

            if( L[i]<=R[j])
            {
                arr[k] = L[i];
                i = i+1;
            }
            else
            {
                arr[k] = R[j];
                j = j+1;

            }           
        }
    }
}