Как выполнить общую сортировку слиянием с помощью компаратора интерфейса Java?

#java #algorithm #arraylist #mergesort

#java #алгоритм #arraylist #сортировка слиянием

Вопрос:

Я пытаюсь объединить списки сортировки, но получаю ArrayOutOfBoundsError и мне не удается это исправить.

Это для общих списков, использующих интерфейс Comparator. Сейчас я запускаю тест с целым числом.

Это мой класс сортировки слиянием:

 public class ListMS<T> {
    private List<T> wholeList;
    private Comparator<T> comparator;

    public ListMS(List<T> list, Comparator<T> C){
        wholeList=new ArrayList<>();
        wholeList.addAll(list);
        comparator=C;
        mergeSort(wholeList, comparator);

    }

    public static <T> void merge(List<T> L1, List<T> L2,List<T> L, Comparator<T> C){
        int i=0;
        int j=0;
        while(i j<L.size()){
            if(j==L2.size() || (i<L.size() amp;amp; C.compare(L1.get(i),L2.get(j))<0)){
                L.set(i j,L1.get(i  ));
            }
            else{
                L.set(i j,L2.get(j  ));
            }
        }
    }

    public static <T> void mergeSort(List<T> L, Comparator<T> C){
        int size=L.size();
        if(size<2){
            return;
        }
        int half=size/2;
        List<T> L1=L.subList(0,half);
        List<T> L2=L.subList(half,size);

        mergeSort(L1,C);
        mergeSort(L1,C);

        merge(L1,L2,L,C);
    }

    public List<T> getWholeList(){
        return wholeList;
    }
}
  

С чем я это тестирую:

 public static void main(String[] args) {

        Comparator<Integer> C=new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        };

        List<Integer> list1K=generateList(1000);
        ListMS<Integer> list1KMerged=new ListMS<>(list1K, C);

        printList(list1K);
        printList(list1KMerged.getWholeList());


    }


    public static List<Integer> generateList(int size){
        List<Integer> listNumber=new ArrayList<>();
        for(int i=0;i<size;i  ){
            int min=0;
            int max=size-1;
            int num=(int)(Math.random() * ((max - min)   1))   min;
            listNumber.add(num);
        }
        return listNumber;
    }

    public static void printList(List<Integer> L){
        for(int i=0;i<L.size();i  ){
            System.out.print(L.get(i));
        }
    }
  

Но я получаю несколько ArrayOutOfBounds:

Исключение в потоке «main» java.lang.Исключение IndexOutOfBoundsException: индекс: 1, размер: 1 в java.util.ArrayList$SubList.rangeCheck(ArrayList.java:1225) в java.util.ArrayList$SubList.get(ArrayList.java: 1042) в ListMS.merge(ListMS.java: 19) в ListMS.mergeSort (ListMS.java: 40) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort (ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.mergeSort(ListMS.java:37) в ListMS.(ListMS.java:11) в TestListMS.main (TestListMS.java: 16)

Комментарии:

1. Какая из них — строка 16?

2. @AniketSahrawat эта строка: ListMS<целое число> list1KMerged=новый ListMS<>(list1K, C);

3. Ваш метод слияния неверен. Вы не можете предполагать, что i и j являются допустимыми индексами L1 и L2 до тех пор, пока i j<L.size() . Вы должны проверить, i достигнуто L1.size() или j достигнуто L2.size() .

4. @Eran Переключает мое while на : while(i<L1.size() amp;amp; j<L2.size()), ты имеешь в виду? Или немного дальше в моих условиях?

5. @EmmaVandeWouwer да, но, кроме того, после цикла вам придется добавить любые оставшиеся элементы L1 или L2 в конец L.

Ответ №1:

В принципе, в ваших счетчиках циклов в merge() methid в этой строке ошибка:

 if(j == L2.size() || (i < L.size() amp;amp; C.compare(L1.get(i), L2.get(j)) < 0))
  

Измените функцию слияния таким образом, чтобы она работала:

 public static <T> void merge(List<T> L1, List<T> L2,List<T> L, Comparator<T> C){
        int i=0;
        int j=0;
        int k=0;
        while(i < L1.size() amp;amp; j < L2.size()) {
            if(C.compare(L1.get(i), L2.get(j)) < 0) {
                L.set(k  , L1.get(i  ));
            }else {
                L.set(k  , L2.get(j  ));
            }   
        }
        while(i < L1.size()) {
            L.set(k  , L1.get(i  ));
        }
        while(j < L2.size()) {
            L.set(k  , L2.get(j  ));
        }
    }
  

Обновить:

В функции сортировки слиянием также есть несколько ошибок. Измените это следующим образом:

 public static <T> void mergeSort(List<T> L, Comparator<T> C){
    int size=L.size();
    if(size<2){
        return;
    }
    int half=size/2;
    List<T> L1=new ArrayList<T>(L.subList(0,half));
    List<T> L2=new ArrayList<T>(L.subList(half,size));

    mergeSort(L1,C);
    mergeSort(L2,C);

    merge(L1,L2,L,C);
    printList(L);
}
  

L1 и L2 не были новыми списками массивов в старом коде. Это были всего лишь два указателя, указывающие на те же ячейки памяти, что и в списке L. Поэтому изменение L в функции слияния также изменило L1 и L2. Чтобы решить эту проблему, вам необходимо создать два новых подмассива с раздельными выделениями памяти.

Также вы вызывали mergeSort(L1,C); дважды вместо того, чтобы вызывать его на L1 и L2 для каждого.

Комментарии:

1. Спасибо за вашу помощь. К сожалению, результат, который я получаю, неверен. Первая половина — это то же повторяющееся число, а вторая половина — в том же порядке, что и исходный список

2. Обновите ответ. В вашей функции сортировки слиянием также были ошибки.

3. Идеально! Большое вам спасибо

4. Хотя сортировка слиянием на месте сложна , это накладные расходы, но нет необходимости перераспределять их при каждом рекурсивном вызове. В непараллельной реализации вы можете получить, используя один буфер размером в половину L . Использование массива для «другого буфера» открыло бы интересную банку с червями. Для ускорения создания экземпляра ArrayList для замены L , но для назначения окончательного слияния, когда L не реализуется RandomAccess , выглядит вариант для оценки. (Часть ожидаемых проблем с производительностью можно обойти с помощью Iterator s.)

5. (Возможно, я имел в виду универсальность , когда вопрос звучит как общий .)