сортировка двусвязного списка с сортировкой слияния
Я нашел этот код в Интернете, и он был для массивов, я хочу изменить его для дважды связанного списка (вместо индекса мы должны использовать указатель), пожалуйста, помогите мне, как я могу изменить метод слияния (я изменил метод сортировки я тоже), это не моя домашняя работа, мне нравится работать со связанным списком !!
public class MergeSort { private DoublyLinkedList LocalDoublyLinkedList; public MergeSort(DoublyLinkedList list) { LocalDoublyLinkedList = list; } public void sort() { if (LocalDoublyLinkedList.size() <= 1) { return; } DoublyLinkedList listOne = new DoublyLinkedList(); DoublyLinkedList listTwo = new DoublyLinkedList(); for (int x = 0; x < (LocalDoublyLinkedList.size() / 2); x++) { listOne.add(x, LocalDoublyLinkedList.getValue(x)); } for (int x = (LocalDoublyLinkedList.size() / 2) + 1; x < LocalDoublyLinkedList.size`(); x++) {` listTwo.add(x, LocalDoublyLinkedList.getValue(x)); } //Split the DoublyLinkedList again MergeSort sort1 = new MergeSort(listOne); MergeSort sort2 = new MergeSort(listTwo); sort1.sort(); sort2.sort(); merge(listOne, listTwo); } private void merge(DoublyLinkedList a, DoublyLinkedList b) { int x = 0; int y = 0; int z = 0; while (x < first.length && y < second.length) { if (first[x] < second[y]) { a[z] = first[x]; x++; } else { a[z] = second[y]; y++; } z++; } //copy remaining elements to the tail of a[]; for (int i = x; i < first.length; i++) { a[z] = first[i]; z++; } for (int i = y; i < second.length; i++) { a[z] = second[i]; z++; } } }
- Quicksort vs heapsort
- Сортировка словаря по ключам
- Какой тип использует Java Collections.sort (узлы)?
- Слияние Сортировка связанного списка
- Сортировка объектов в массиве по дате
- Как реализовать classические алгоритмы сортировки в современном C ++?
- ранг и порядок в R
- Почему в Java нет SortedList?
- Сортировка многомерного массива в VBA
- сортировка 2D-массива String в java
- Какой самый быстрый алгоритм сортировки связанного списка?
- Как сортировать аррайалист объектов по свойству?
- Что такое стабильность в алгоритмах сортировки и почему это важно?
Сортировка сортировки требует довольно часто разбивать список. Не повторяется ли в середине LinkedList довольно дорогостоящая операция, которую вы можете выполнить на нем (ну, не сортируя ее)? Я мог видеть, что шаг слияния работает очень хорошо (вы повторяете переходы по двум связанным спискам), но я не уверен, что эта реализация стоит проблем без операции O (1) split.
Следовать за
Как указывалось выше, операция разделения O (n) на самом деле не усложняет задачу, когда вы уже выполняете O (n) вещи во время фазы слияния. Тем не менее, вы по-прежнему сталкиваетесь с трудностями при выполнении итераций, как вы делаете (не используя Iterator
а вместо этого используйте get
в List
со слабыми характеристиками произвольного доступа).
Мне было скучно при отладке какой-то другой проблемы, поэтому написал вам то, что я считаю достойной реализацией Java этого алгоритма. Я следовал за псевдокодом Wikipedia дословно и посыпался некоторыми родовыми и печатными заявлениями. Если у вас есть какие-либо вопросы или проблемы, просто спросите.
import java.util.List; import java.util.LinkedList; /** * This class implements the mergesort operation, trying to stay * as close as possible to the implementation described on the * Wikipedia page for the algorithm. It is meant to work well * even on lists with non-constant random-access performance (ie * LinkedList), but assumes that {@code size()} and {@code get(0)} * are both constant-time. * * @author jasonmp85 * @see Merge sort */ public class MergeSort { /** * Keeps track of the call depth for printing purposes */ private static int depth = 0; /** * Creates a list of 10 random Longs and sorts it * using {@link #sort(List)}. * * Prints out the original list and the result. * */ public static void main(String[] args) { LinkedList list = new LinkedList (); for(int i = 0; i < 10; i++) { list.add((long)(Math.random() * 100)); } System.out.println("ORIGINAL LIST\n" + "=================\n" + list + "\n"); List sorted = sort(list); System.out.println("\nFINAL LIST\n" + "=================\n" + sorted + "\n"); } /** * Performs a merge sort of the items in {@code list} and returns a * new List. * * Does not make any calls to {@code List.get()} or {@code List.set()}. * * Prints out the steps, indented based on call depth. * * @param list the list to sort */ public static > List sort(List list) { depth++; String tabs = getTabs(); System.out.println(tabs + "Sorting: " + list); if(list.size() <= 1) { depth--; return list; } List left = new LinkedList (); List right = new LinkedList (); List result = new LinkedList (); int middle = list.size() / 2; int added = 0; for(T item: list) { if(added++ < middle) left.add(item); else right.add(item); } left = sort(left); right = sort(right); result = merge(left, right); System.out.println(tabs + "Sorted to: " + result); depth--; return result; } /** * Performs the oh-so-important merge step. Merges {@code left} * and {@code right} into a new list, which is returned. * * @param left the left list * @param right the right list * @return a sorted version of the two lists' items */ private static > List merge(List left, List right) { String tabs = getTabs(); System.out.println(tabs + "Merging: " + left + " & " + right); List result = new LinkedList (); while(left.size() > 0 && right.size() > 0) { if(left.get(0).compareTo(right.get(0)) < 0) result.add(left.remove(0)); else result.add(right.remove(0)); } if(left.size() > 0) result.addAll(left); else result.addAll(right); return result; } /** * Returns a number of tabs based on the current call depth. * */ private static String getTabs() { StringBuffer sb = new StringBuffer(""); for(int i = 0; i < depth; i++) sb.append('\t'); return sb.toString(); } }
Бежать
- Сохраните код в файл с именем MergeSort.java
- Запустить
javac MergeSort.java
- Запустить
java MergeSort
- диво
- При необходимости запустите
javadoc -private MergeSort.java
чтобы создать документацию. Откройте файл index.html, который он создает.
Это зависит от того, что DoublyLinkedList
– это конкретный пользовательский тип или просто псевдоним для связанного типа списка?
В первом случае вы должны иметь индексированные методы get / set и / или iterator, определенные в нем, что делает задачу простой.
В последнем случае, почему бы не использовать стандартный java.util.LinkedList
?
С точки зрения интерфейса List
эта операция может быть реализована следующим образом:
List merge(List first, List second, List merged) { if (first.isEmpty()) merged.adAll(second); else if (second.isEmpty()) merged.adAll(first); else { Iterator firstIter = first.iterator(); Iterator secondIter = second.iterator(); T firstElem = firstIter.next(); T secondElem = secondIter.next(); do { if (firstElem < secondElem) { merged.add(firstElem); firstElem = firstIter.hasNext() ? firstIter.next() : null; } else { merged.add(secondElem); secondElem = secondIter.hasNext() ? secondIter.next() : null; } } while (firstIter.hasNext() && secondIter.hasNext()); //copy remaining elements to the tail of merged if (firstElem != null) merged.add(firstElem); if (secondElem != null) merged.add(secondElem); while (firstIter.hasNext()) { merged.add(firstIter.next()); } while (secondIter.hasNext()) { merged.add(secondIter.next()); } } }
Эта реализация является немного более утомительной, чем при использовании массивов, главным образом, поскольку iteratorы «потребляются» next
операцией, поэтому необходимо вести учет текущего элемента в каждом списке. С get
, код будет проще, очень похож на решение массива, однако для больших списков он будет медленнее, как отметил @ sepp2k.
Еще несколько заметок:
- традиция Java - использовать имена переменных нижнего регистра, поэтому
localDoublyLinkedList
- У Java нет указателей, только ссылок.
Вчера я столкнулся с этой проблемой. Вот несколько мыслей.
Сортировка DoublyLinkedList
отличается от сортировки Array
поскольку вы не можете ссылаться на индексные ссылки на любой произвольный элемент в списке. Вместо этого вам нужно запомнить элементы во время каждого рекурсивного шага, а затем передать их функции слияния. Для каждого этапа рекурсии вам нужно запомнить только первый элемент из каждой половины списка. Если вы не помните эти элементы, вы быстро получите индексы, но это приведет вас к проблеме, которая в вашей функции merge
вам нужно пройти весь список с помощью -loops, чтобы найти элементы для слияния. Это, в свою очередь, означает, что вы получаете сложность O(n^2)
.
Другим важным моментом является шаг рекурсии в список и разделение списка на две половины. Вы можете сделать этот шаг в рекурсивной части, используя for
-loops. В отличие от merge
на этом этапе for
-loops приведет только к сложности O(log(n) * n/2)
и это все еще ниже общей сложности O(n*log(n))
. Вот почему:
-
Вам всегда нужно найти первый элемент каждой половины части списка.
-
На первом этапе рекурсии вам необходимо передать
first
элемент и элемент в позицииn/2
. Для этого требуетсяn/2
шага. -
На каждом следующем шаге вам нужно найти средний элемент для каждой из двух половин списка, который дает нам
n/4
чтобы найти элемент в первой половине иn/4
в другой половине. В общей сложности этоn/2
. -
В каждом последующем рекурсивном шаге количество частей списка удваивается, а длины разделяются на два:
-
4 * n/8
на 3-й глубине рекурсии -
8 * n/16
на 4-й глубине рекурсии и т. Д. …
-
-
Глубина рекурсии –
log(n)
и на каждом шаге мы выполняемn/2
шага. Это равноO(log(n)*n/2)
Наконец, вот какой код:
public DoublyLinkedList mergesort(DoublyLinkedList in, int numOfElements) { in.first = mergesort(in.first, numOfElements); return in; }
Сортировка слиянием:
public ListElement mergesort(ListElement first, int length) { if(length > 1) { ListElement second = first; for(int i=0; i
и объединить:
public ListElement merge(ListElement first, ListElement second, int length) { ListElement result = first.prev; //remember the beginning of the new list will begin after its merged int right = 0; for(int i=0; i
Максимальный объем используемой памяти также довольно низкий (не включая сам список). Исправьте меня, если я ошибаюсь, но он должен быть меньше 400 байт (на 32-битной). Это будет 12 байт за звонок на mergeSort раз глубина рекурсии log (n) плюс 20 байт для переменных слияния, таким образом: 12 * log (n) +20 байт.
PS Code проверен на 1 миллион элементов (занимает 1200 мс). Также DoublyLinkedList
- это контейнер, в котором хранится первый ListElement
списка.
Обновление: я ответил на аналогичный вопрос о Quicksort, используя те же структуры данных, однако по сравнению с этой реализацией Mergesort он работает намного медленнее. Ниже приведены некоторые обновленные сроки для ссылки:
Сортировка слиянием:
1.000.000 Items: 466ms 8.300.000 Items: 5144ms
Quicksort:
1.000.000 Items: 696ms 8.300.000 Items: 8131ms
Обратите внимание, что тайминги относятся к моему оборудованию, и вы можете получить разные результаты.
Прежде всего, вы НЕ должны использовать индексы при работе со связанными списками. Делай это так:
while (i < in.size/2){ listOne.addLast( in.remove(in.first()) ); i++ } while(!in.isEmptly){ listTwo.addLast( in.remove(in.first()) ); }
вwhile (i < in.size/2){ listOne.addLast( in.remove(in.first()) ); i++ } while(!in.isEmptly){ listTwo.addLast( in.remove(in.first()) ); }
И для слияния
merge(a, b, out){ while(!a.empty && !b.empty){ if(a.first() >= b.first()) out.addLast( a.remove(a.first()) ); else out.addLast( b.remove(b.first()) ); //remember to take care of the remaining elements while(!a.empty) out.addLast( a.remove(a.first()) ); while(!b.empty) out.addLast( b.remove(b.first()) ); }
вmerge(a, b, out){ while(!a.empty && !b.empty){ if(a.first() >= b.first()) out.addLast( a.remove(a.first()) ); else out.addLast( b.remove(b.first()) ); //remember to take care of the remaining elements while(!a.empty) out.addLast( a.remove(a.first()) ); while(!b.empty) out.addLast( b.remove(b.first()) ); }
вmerge(a, b, out){ while(!a.empty && !b.empty){ if(a.first() >= b.first()) out.addLast( a.remove(a.first()) ); else out.addLast( b.remove(b.first()) ); //remember to take care of the remaining elements while(!a.empty) out.addLast( a.remove(a.first()) ); while(!b.empty) out.addLast( b.remove(b.first()) ); }
Таким образом, все равно будет O (n log n)
Другая идея – создать массив со всеми элементами списка, отсортировать массив и затем снова вставить элементы в список.
Pro: очень просто реализовать, быстрее, если плохая реализация списка mergesort (возможно, также быстрее, чем хорошие реализации)
Contra: использует дополнительное пространство (O (n))