/* * Sorter.java * * Copyright (C) 2011 Leo Osvald <leo.osvald@gmail.com> * * This file is part of SGLJ. * * SGLJ is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SGLJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see <http://www.gnu.org/licenses/>. */ package org.sglj.util; import org.sglj.math.BinaryPredicate.IntBinaryPredicate; /** * Utility class with various sorting algorithm on primitive types, with * custom comparators. * * @author Leo Osvald * @version 0.7 */ public class Sorter { public static void countingSort(int[] a, int min, int max, int from, int to) { IntSorter.checkMinMax(min, max); ArrayUtils.rangeCheck(a, from, to); IntSorter.countingSort0(a, min, max, from, to); } public static void countingSort(int[] a, int from, int to) { ArrayUtils.rangeCheck(a, from, to); countingSort(a, from, to, ArrayUtils.min(a, from, to), ArrayUtils.max(a, from, to)); } public static void heapSort(int[] a, int from, int to) { ArrayUtils.rangeCheck(a, from, to); IntSorter.heapSort(a, from, to); } public static void heapSort(int[] a, int from, int to, final IntBinaryPredicate cmp) { ArrayUtils.rangeCheck(a, from, to); IntSorter.heapSort(a, from, to, cmp); } static final int INSERTIONSORT_THRESHOLD = 8; static class TypeSorter { // static <T> void countingSort0(int[] a, T min, T max, int first, int last) { // int[] count = new int[max - min + 1]; // for (int i = first; i < last; ++i) // ++count[a[i] - min]; // for (int x = max - min; x >= 0; --x) { // for (int i = count[x]; i > 0; --i) // a[--last] = x + min; // } // } } static class IntSorter { static void countingSort0(int[] a, int min, int max, int first, int last) { int[] count = new int[max - min + 1]; for (int i = first; i < last; ++i) ++count[a[i] - min]; for (int x = max - min; x >= 0; --x) { for (int i = count[x]; i > 0; --i) a[--last] = x + min; } } static void mergeSort(int[] a, int from, int to) { int len = to - from; int[] tmp = new int[len]; System.arraycopy(a, from, tmp, 0, len); mergeSort0(tmp, a, from, to, from); } private static void mergeSort0(int[] tmp, int[] sorted, int first, int last, int offset) { int len = last - first; // insertion sort on small arrays if (len < INSERTIONSORT_THRESHOLD) { insertionSort(sorted, first, last); return ; } int sortedFirst = first; int sortedLast = last; first -= offset; last -= offset; int mid = (first + last) >>> 1; mergeSort0(sorted, tmp, first, mid, -offset); mergeSort0(sorted, tmp, mid, last, -offset); if (tmp[mid - 1] < tmp[mid]) { // if already merged System.arraycopy(tmp, first, sorted, sortedFirst, len); } else { // merge sorted halves for (int p = first, q = mid, i = sortedFirst; i < sortedLast; ++i) { if (q >= last || p < mid && !(tmp[q] < tmp[p])) sorted[i] = tmp[p++]; else sorted[i] = tmp[q++]; } } } static void insertionSort(int[] a, int first, int last) { for (int cur = first; ++cur < last; ) { int key = a[cur]; int i = cur; for (int val = a[i - 1]; key < val; val = a[--i - 1]) { a[i] = val; if (first == i - 1) { --i; break; } } a[i] = key; } } static void mergeSort(int[] a, int from, int to, final IntBinaryPredicate cmp) { int len = to - from; int[] tmp = new int[len]; System.arraycopy(a, from, tmp, 0, len); mergeSort0(tmp, a, from, to, from, cmp); } private static void mergeSort0(int[] tmp, int[] sorted, int first, int last, int offset, final IntBinaryPredicate cmp) { int len = last - first; // insertion sort on small arrays if (len < INSERTIONSORT_THRESHOLD) { insertionSort(sorted, first, last, cmp); return ; } int sortedFirst = first; int sortedLast = last; first -= offset; last -= offset; int mid = (first + last) >>> 1; mergeSort0(sorted, tmp, first, mid, -offset, cmp); mergeSort0(sorted, tmp, mid, last, -offset, cmp); if (cmp.holds(tmp[mid - 1], tmp[mid])) { // if already merged System.arraycopy(tmp, first, sorted, sortedFirst, len); } else { // merge sorted halves for (int p = first, q = mid, i = sortedFirst; i < sortedLast; ++i) { if (q >= last || p < mid && !cmp.holds(tmp[q], tmp[p])) sorted[i] = tmp[p++]; else sorted[i] = tmp[q++]; } } } static void insertionSort(int[] a, int first, int last, final IntBinaryPredicate cmp) { for (int cur = first; ++cur < last; ) { int key = a[cur]; int i = cur; for (int val = a[i - 1]; cmp.holds(key, val); val = a[--i - 1]) { a[i] = val; if (first == i - 1) { --i; break; } } a[i] = key; } } static void heapSort(int[] a, int first, int last) { int len = last - first; // max-heapify in O(last - first) for (int root = (len >>> 1) - 1; root >= 0; --root) downHeap(a, first, len, root); // sort by extraction of max while (len-- > 1) { --last; int tmp = a[last]; a[last] = a[first]; a[first] = tmp; downHeap(a, first, len, first); } } private static void downHeap(int[] a, int first, int len, int root) { int rootVal = a[root + first]; int p = root; int halfLen = (len >>> 1); while (p < halfLen) { int child = (p << 1) + 1; int right = child + 1; int childVal = a[child + first]; if (right < len && a[right + first] > childVal) childVal = a[(child = right) + first]; if (rootVal >= childVal) break; a[p + first] = childVal; p = child; } a[p + first] = rootVal; } static void heapSort(int[] a, int first, int last, final IntBinaryPredicate cmp) { int len = last - first; // max-heapify in O(last - first) for (int root = (len >>> 1) - 1; root >= 0; --root) downHeap(a, first, len, root, cmp); // sort by extraction of max while (len-- > 1) { --last; int tmp = a[last]; a[last] = a[first]; a[first] = tmp; downHeap(a, first, len, first, cmp); } } private static void downHeap(int[] a, int first, int len, int root, final IntBinaryPredicate cmp) { int rootVal = a[root + first]; int p = root; int halfLen = (len >>> 1); while (p < halfLen) { int child = (p << 1) + 1; int right = child + 1; int childVal = a[child + first]; if (right < len && cmp.holds(childVal, a[right + first])) childVal = a[(child = right) + first]; if (!cmp.holds(rootVal, childVal)) break; a[p + first] = childVal; p = child; } a[p + first] = rootVal; } private static void checkMinMax(int min, int max) { if (min > max) throw new IllegalArgumentException(); } } }