#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. (Возможно, я имел в виду универсальность , когда вопрос звучит как общий .)