
Алгоритмы написаны на Scala 2.12 в java стиле.
Задача может быть сразу в нескольких разделах.
Личный блог. Заметки о программировании и не только
package oracle_sort; import java.util.*; import org.junit.runner.JUnitCore; import org.junit.runner.Result; /* Сортировка слиянием: * Сложность: N*Log2(N) * Доп. память: O(N) * Общий смысл: ** рекурсивно разбиваем массив на 2 части, пока размер элемента не дойдет до N (тут =1 ) ** сливаем массивы в один последовательным проходом (в худшем случае N+M), сравнивая элементы */ class MergeSort { //проверка достаточности разбиения @SuppressWarnings("rawtypes") protected boolean isSortArr(Comparable[] arr) { if(arr.length <= 1) return true; return false; } //разбитие массива на 2 равные части @SuppressWarnings("rawtypes") public void sort(Comparable[] arr) { //если массив достаточно разбит, то прерываем рекурсию if(isSortArr(arr)) return; //левая половина Comparable[] left = new Comparable[arr.length / 2]; System.arraycopy(arr, 0, left, 0, left.length); sort(left); //swop to disk //правая половина Comparable[] right = new Comparable[arr.length - left.length]; System.arraycopy(arr, left.length, right, 0, right.length); sort(right); //слияние merge(left, right, arr); } //sort } //MergeSort /* * Комбинированная сортировка: слиянием + быстрая + поразрядная * * */ public class OracleSort extends MergeSort { //размер памяти под сортировку int sort_area_size = 1; //размер ключа int avg_key_size = 0; final double log10_2 = Math.log(2); OracleSort(int _sort_area_size, int _avg_key_size) { sort_area_size = _sort_area_size; avg_key_size = _avg_key_size; } //OracleSort protected boolean isSortArr(Comparable[] arr) { if(arr.length < 2) return true; //если после разбиения массива размер меньше размера под сортировку if(arr.length <= sort_area_size) { //если Integer и размер ключа меньше log2(Т), то делаем поразрядную сортировку if( arr[0].getClass() == Integer.class && avg_key_size > 0 && avg_key_size < ( Math.log(arr.length) / log10_2 ) ) { Integer[] _arr = new Integer[arr.length]; //как привести Comparable к Integer? System.arraycopy(arr, 0, _arr, 0, arr.length); Sorts.radixSortUInt(_arr); System.arraycopy(_arr, 0, arr, 0, arr.length); _arr = null; } else { //иначе быструю сортировку Sorts.QSort(arr, 0, arr.length-1); } return true; } return false; } //isSortArr }
/* Быстрая сортировка: * Сложность: N*Log2(N) * Доп. память: нет * Минусы: * - Сильно зависит от выбора средней точки, при неправильно выборе (крайний элемент) сложность = N*N * - На большом наборе данных из-за рекурсии может вызвать переполнение стека * Общий смысл: ** находим элемент в центре массива со средним значением ** движемся с краев к центру, меняя значения местами, если влевой части значение больше середины или вправой больше ** при доходе до середины, запускам рекурсивно для левой и правой части */ public static <T extends Comparable<T>> void QSort(T[] arr, int l, int r) { int i = l; int j = r; //средний элемент = левая граница + (правая граница - левая граница) / 2 T c = arr[l + ( r - l ) / 2]; // идем к центу, пока края не встретятся while(i < j) { //идем с левого края к центру, пока середина больше текущего значения (ищем элемент больше середины) while(arr[i].compareTo(c) < 0) { i++; } //с правого края к центру, пока середина меньше текущего значения (ищем элемент меньше середины) while(arr[j].compareTo(c) > 0) { j--; } //если левый индекс меньше правого индекса, то меняем местами и смещаемся if(i <= j) { T t = arr[j]; arr[j] = arr[i]; arr[i] = t; i++; j--; } } //рекурсивно запускаемся для левой и правой части if(l < j) { QSort(arr, l, j); } if(r > i) { QSort(arr, i, r); } } //QSort
/* Поразрядная сортировка: * Сложность: N*K ** где K число разрядов в сортиуемом элементе * Доп. память: O(N) * Минусы/Плюсы: * -/+ быстрей, если k < log2(n) и дольше в других случаях * - разный размер хэш массива для разных типов данных * Особенность: * Порядок сортировки зависит от направления: для цифр нужно справа-навлево, для строк: слева-направо * Общий смысл: * Идем по рязрядам числа справа-налево и помещаем элементы в массив по значению текущего разряда ** т.е. элементы сначала упорядычиваются по последнему элементу ** потом переупорядычиваваются по второму и т.д. пока полностью не упорядочим массив */ public static <T extends Integer> void radixSortUInt(T[] arr) { //хэш массив из 10 элементов для 10-ричного числа final int RADIX = 10; List<T>[] bucket = new ArrayList[RADIX]; for (int i = 0; i < bucket.length; i++) { //список чисел, содержащих нужно число в разряде bucket[i] = new ArrayList<T>(); } //признак, что все разряды перебраны boolean maxLength = false; //значение в разряде int rank_val = -1; //номер разряда int placement = 1; //пока не перебраны все разряды while (!maxLength) { maxLength = true; //для каждого элемента массива for (int i = 0; i < arr.length; i++) { //добавляем элемент в массив по значению в разряде rank_val = arr[i] / placement; bucket[rank_val % RADIX].add(arr[i]); //если в разряде не пусто, то ставим флаг повторения цикла if (maxLength && rank_val > 0) { maxLength = false; } } //разворачиваем двухуровневый массив обратно в последовательный int a = 0; for (int b = 0; b < RADIX; b++) { for (int i = 0; i < bucket[b].size(); i++) { arr[a++] = bucket[b].get(i); } bucket[b].clear(); } //переходим к следующему разряду placement *= RADIX; } } //radixSortUInt
//слияние 2 упорядоченных массивов в один @SuppressWarnings({ "rawtypes", "unchecked" }) private void merge(Comparable[] left, Comparable[] right, Comparable[] arr) { int iFirst = 0; int iSecond = 0; int iMerged = 0; //бежим, пока не дойдем до конца одного из массивов while (iFirst < left.length && iSecond < right.length) { //если элемент в левом массиве больше, чем в правом if (left[iFirst].compareTo(right[iSecond]) < 0) { //то добавляем элемент из левого arr[iMerged] = left[iFirst]; //и двигаемся в левом на 1 iFirst++; } else { //иначе добавляем элемент из правого arr[iMerged] = right[iSecond]; //двигаемся в правом на 1 iSecond++; } //в любом случае увеличиваем результирующий массив iMerged++; } //оставшиеся элементы - больше последнего (максимального) элемента одного из массивов. Докопируем оставшиеся элементы. System.arraycopy(left, iFirst, arr, iMerged, left.length - iFirst); System.arraycopy(right, iSecond, arr, iMerged, right.length - iSecond); } //merge