дополнительная сортировка слияния - PullRequest
0 голосов
/ 19 мая 2010

Мне нужно сделать сортировку слиянием, используя дополнительный массив. Вот мой код:

public class extra_storage{  
    public static void main(String[]args) { 

        int x[]=new int[]{12,9,4,99,120,1,3,10};
        int a[]=new int[x.length];

        mergesort(x,0,x.length-1,a);
        for (int i=0;i<x.length;i++){
            System.out.println(x[i]);
        }       
    }

    public static void mergesort(int x[],int low,int high, int a[]){      
        if (low>=high) {
            return;
        }
        int middle=(low+high)/2;
        mergesort(x,low,middle,a);
        mergesort(x,middle+1,high,a);
        int k;
        int lo=low;
        int h=high;
        for (k=low;k<high;k++)
            if ((lo<=middle ) && ((h>high)||(x[lo]<x[h]))){
                a[k]=x[lo++];
            }
            else {
                a[k]=x[h++];
            }
        for (k=low;k<=high;k++){
            x[k]=a[k];
        }     
    }
}

Но что-то не так. Когда я запускаю его, вывод такой:

1
0
3
0
4
0
9
0

В чем проблема?

Ответы [ 3 ]

2 голосов
/ 19 мая 2010

Вот ваш оригинальный алгоритм с некоторыми исправлениями и стилистическими улучшениями:

public class MergeSort {  
    public static void main(String[]args) { 
        int[] nums = {12,9,4,99,120,1,3,10};
        mergeSort(nums);
        System.out.println(java.util.Arrays.toString(nums));
        // "[1, 3, 4, 9, 10, 12, 99, 120]"
    }
    static void mergeSort(int[] arr) {
        mergeSort(arr, 0, arr.length - 1, new int[arr.length]);
    }
    static void mergeSort(int[] arr, int low, int high, int[] buff){
        if (low >= high) {
            return;
        }
        int mid = (low + high) >>> 1;
        mergeSort(arr, low, mid, buff);
        mergeSort(arr, mid+1, high, buff);
        for (int left = low, right = mid + 1, i = low; i <= high; i++) {
            if (right > high || left <= mid && arr[left] <= arr[right]) {
                buff[i] = arr[left++];
            } else {
                buff[i] = arr[right++];
            }
        }
        for (int i = low; i <= high; i++) {
            arr[i] = buff[i];
        }
    }
}

В отличие от реализации Eyala, где роли src и dst меняются местами по уровням рекурсии, здесь мы всегда сортируем один и тот же объект массива arr, а объект массива buff равен всегда используется только как временный буфер для слияния (и, следовательно, после фазы слияния существует фаза копирования). Это все еще O(N log N), но более продвинутая реализация Eyal'а будет постоянным улучшением.

В цикле объединения

По существу, у вас есть индекс left для левого подмассива и индекс right для правого подмассива, и вы выбираете правый элемент из left или right для помещения в buff.

Допустимый диапазон элементов (включая границы):

  • left = low...mid для левого подмассива
  • right = mid+1...high для правого подмассива

Чтобы оценить, какой элемент выбрать, рассмотрите условие, при котором выбран элемент left. Это происходит, когда:

  • Больше нет элементов для выбора из правого подмассива (т. Е. right > high)
  • ИЛИ (условно) все еще есть элемент, который можно выбрать из левого подмассива (т. Е. left <= mid) и (условно) этот элемент меньше или равен элементу из правый подмассив (т.е. arr[left] <= arr[right]).

Здесь важно использовать операторы условного и && ( JLS 15.23 ) и условного или || ( JLS 15.24 ) и заказать их условия соответственно. В противном случае вы получите ArrayIndexOutOfBoundsException.

Смежные вопросы


Нахождение среднего между двумя числами

Обычно можно увидеть следующее:

int mid = (low + high) / 2; // BROKEN! Can result in negative!

Проблема заключается в том, что в настоящее время массивы / списки и т. Д. Могут легко превышать 2 30 элементов, а приведенное выше может вызвать переполнение и привести к отрицательному числу.

Новая идиома, предложенная Джошем Блохом, такова:

int mid = (low + high) >>> 1; // WORKS PERFECTLY!

При этом используется беззнаковый оператор смещения вправо ( JLS 15.19 ); он корректно обрабатывает любые переполнения при добавлении.

Ссылки

Похожие вопросы


В объявлениях массива

Не создавайте привычку объявлять массивы так:

int x[];

Вместо этого вы должны поставить скобки с типом , а не с идентификатором :

int[] x;

Смежные вопросы

1 голос
/ 19 мая 2010

Ваш код недостаточно понятен, и в нем много несоответствующих операций. Кроме того, он не демонстрирует поведение, которое вы описываете.

Идея версии слияния, которую вы пытаетесь реализовать, состоит в том, чтобы использовать один вспомогательный массив ( исходный массив ) того же размера, что и входной массив ( целевой массив ). Это позволяет объединять из одного массива в другой, так как нет эффективного метода объединения на месте. Алгоритм состоит из сортировки двух половинок массива назначения в соответствующие диапазоны в исходном массиве и последующего слияния этих двух половинок в массив назначения. Обратите внимание, что для этого требуется, чтобы при каждом вызове два массива были идентичны в диапазоне, заданном low и high.

Ниже приведена такая реализация для массивов int. Вы можете добавить оптимизации, такие как вставка сортировки для небольших входных данных или добавление половинок вместо их объединения, когда это возможно. Этот вид оптимизации можно найти в реализации Arrays.sort (Object []) .

public static void mergeSort(int[] arr){
    int[] aux = arr.clone();
    mergeSort(aux, 0, arr.length, arr);
}

private static void mergeSort(int[] src, int low,  int high, int[] dst) {
    // One or no items - nothing to sort  
    if (high-low<=1)
        return;

    // Recursively sorting into src
    int mid = (low + high) / 2;
    mergeSort(dst, low, mid, src);
    mergeSort(dst, mid, high, src);

    // Merge halves into dst
    for(int i = low, p = low, q = mid; i < high; i++) {
        if (q >= high || p < mid && src[p] <= src[q])
            dst[i] = src[p++];
        else
            dst[i] = src[q++];
    }
}
1 голос
/ 19 мая 2010

Похоже, у вас переполнение стека.

В вашем коде

public static void mergesort(int x[],int low,int high, int a[]){      
    if (low>high) {
        return;
    }
    int middle=(low+high)/2;
    mergesort(x,low,middle,a);
    mergesort(x,middle+1,high,a);

Если низкий уровень начинается с нижнего или равного высокого, то в конечном итоге он становится равным высокому, в этом случае средний == низкий == высокий и он будет называть себя вечно.


Вопрос был изменен, чтобы удалить переполнение стека после отправленного ответа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...