/** * Copyright © 2014 Mattias Andrée (maandree@member.fsf.org) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package algorithms.searching; import java.util.*; /** * Hybrid binary search class. Binary search runs in logarithmic time, constant * memory, and requires the list to be sorted. Binary search often out preforms * linear search, interpolation sort however often out preforms binary search * for lists with smooth distribution. Hybrid binary search uses binary search * and falls back to linear search when the number of elemetns left are small * enough. Identity search is not possible, only equality search. Null elements * are not allowed, unless the specified compator allows it. */ public class HybridBinarySearch { /** * All elements in the array is the searched for item */ public static final int EVERY_ELEMENT = -1; /** * Item was not on the edges, but may be inside * Values lower than this value indicate that the value does * not exist. */ public static final int MAYBE = -2; /** * The item's value is smaller than the smallest in the array. * This value and lower values indicate that the value does * not exist. */ public static final int TOO_SMALL = -3; /** * The item's value is larger than the largest in the array */ public static final int TOO_LARGE = -4; /** * List sort order */ public static enum SortOrder { /** * Bigger index, bigger value */ ASCENDING, /** * Bigger index, smaller value */ DESCENDING, } /** * List sort order */ public static enum SearchMode { /** * Look for the index of the easiest to find occurence */ FIND_ANY, /** * Look for the index of the first occurence */ FIND_FIRST, /** * Look for the index of the last occurence */ FIND_LAST, /** * Look for both the index of the fist occurence and of the last occurence.
* The returned value will be {@code (LAST << 32) | FIRST}. */ FIND_FIST_AND_LAST, } £>for T in boolean char byte short int long float double T T++; do . src/comparable /** * Gets whether an item may be contained by a list * * @param item The item for which to search * @param array The list in which to search * @param order The list's element order * @return {@link #MAYBE}, {@link #TOO_SMALL}, {@link #TOO_LARGE}, {@link #EVERY_ELEMENT} * or the index of a(!) found item [first or last position] */ public static £(fun "int" contains "${T} item, ${Tarray} array, SortOrder order") { /* This is identical to as in BinarySearch */ int low = £(cmp "array[0]" "item"); if (order == SortOrder.ASCENDING) { if (low > 0) return TOO_SMALL; int high = £(cmp "array[1]" "item"); if (low == 0) return high == 0 ? EVERY_ELEMENT : 0; return high == 0 ? array.length - 1 : high < 0 ? TOO_LARGE : MAYBE; } { if (low < 0) return TOO_SMALL; int n = array.length - 1; int high = £(cmp "array[n]" "item"); if (low == 0) return high == 0 ? EVERY_ELEMENT : 0; return high == 0 ? n : high > 0 ? TOO_LARGE : MAYBE; } } /** * Finds the index of the first occurance of an item in a list * * @param item The item for which to search * @param array The list in which to search * @param start Offset for the list or search range * @param end End of the list or search range * @return The index of the first occurance of the item within the list, {@code -1} if it was not found */ private static £(fun "int" linearFirst "${T} item, ${T}[] array, int start, int end") { /* This is nearly identical to LinearSearch.indexOfFirst */ int i = start < 0 ? (array.length - start) : start; int n = end < 0 ? (array.length - end) : end; for (;;) { if (i == n) break; if (£(equal "array[i]" item)) return i; i++; } return -1; } /** * Finds the first, last or any occurance of an item in a list * * @param item The item for which to search * @param array The list in which to search * @param order The list's element order * @param mode The search mode * @param fallback The number of elements at when to fall back to linear search * @return The index of the found item, if not mode does not say otherwise, or, if not * found, the bitwise negation of the position to which it should be inserted */ public static £(fun "long" indexOf "${T} item, ${Tarray} array, SortOrder order, SearchMode mode, int fallback") { £{Telement} x; int min = 0, mid, rc = -1; int max = array.length - 1; £>function f £>{ if (mode == SearchMode.£{1}) for (;;) { if (min + fallback >= max) return linearFirst(item, array, min, max); if (item == (x = array[mid = (min + max) >>> 1])) £{2}; /* NB! (x R item), instead of (item R x) */ if (£(${3} x item)) min = mid + 1; else max = mid - 1; if (min > max) return £{4}; } £>} £>function p £>{ for (;;) { £>if [ $3 = 0 ]; then if (min + fallback >= max) { first = last = rc = linearFirst(item, array, min, max); easyMin = first + 1; easyMax = max; normal = true; if (last >= 0) break; } £>else if (!normal && (min + fallback >= max)) { first = last = rc = linearFirst(item, array, min, max); normal = true; if (last >= 0) { easyMin = first + 1; easyMax = max; break; } } £>fi if (item == (x = array[mid = (min + max) >>> 1])) { rc = mid; £>if [ $3 = 0 ]; then if (easyMin == -1) { easyMax = mid - 1; easyMin = min; } £>fi } /* NB! (x R item), instead of (item R x) */ if (£(${1} x item)) min = mid + 1; else max = mid - 1; if (min > max) { if (rc < 0) return ~((long)min); £{2} = rc; break; } } £>} £>function _ £>{ { £>f FIND_ANY 'return (long)mid' "${1}" '~((long)min)' £>f FIND_FIRST 'rc = mid' "${1}" 'rc < 0 ? ~((long)min) : (long)rc' £>f FIND_LAST 'rc = mid' "${1}=" 'rc < 0 ? ~((long)min) : (long)rc' int easyMin = -1, easyMax = -1, first, last; boolean normal = false; £>p "${1}" first 0 min = easyMin; max = easyMax; £>p "${1}=" last 1 return (((long)last) << 32) | (long)first; } £>} if (order == SortOrder.ASCENDING) £>_ 'less' £>_ 'greater' } £>done }