I tried to write a decent answer to Most efficient way to get the largest 3 elements of an array using no comparisons but a procedure for ordering 5 elements descendantly.
I could not come up with something concise, let alone compelling.
I assumed duplicate values were allowed; unique values allowing "multidimensional trickery", code would probably end up more involved, still.
I coded around order() ordering non-descendingly; one thing nagging me is I might have been better off choosing non-ascendingly.
package net.reserves.human.coder.greybeard.sandbox; import java.util.Arrays; /** Determine top t values given a procedure to order k values. */ // currently for t < k; thinking k =(<) t gets (much) more complicated, still public class TopByOrderK<E> { /** <code>apply(values, first, count)</code> * orders <code>count</code> values in <code>E [] values</code> * from <code>first</code> non-descendingly. */ public interface OrderK<E> { /** <code>apply(values, first, count)</code> * orders <code>count</code> values in <code>E [] values</code> * from <code>first</code> non-descendingly. */ public default void apply(E[] values, int offset, int k) { Arrays.sort(values, offset, offset + k); } } public TopByOrderK(int top, int k, OrderK<E> order) { if (k <= top) throw new IllegalArgumentException("number of values" + " that can be ordered by a single call shall exceed" + " the number of top values to determine."); this.t = top; t_ = top -1; this.k = k; k_ = k - 1; k_t = k - top; this.order = null == order ? new OrderK<E>() {} : order; } OrderK<E> order; int calls; void orderK(E[] values, int offset) { calls += 1; order.apply(values, offset, k); } int offsets[], t, t_, k, k_, k_t; E[] candidates[]; E nonTopT; /** From <code>valid</code> valid values * in <code>candidates[--keep]</code>, distribute up to * <code>keep</code> values to this and "lower priority" * <code>candidates</code> arrays */ void keep(int keep, int valid) { final E values[] = candidates[--keep]; orderK(values, 0); nonTopT = values[0]; int down = k_ - keep; while (0 <= --keep && 0 < --valid) { candidates[keep][offsets[keep]] = values[down + keep]; if (k <= (offsets[keep] += 1)) { if (0 < keep) keep(keep, k); else { orderK(candidates[0], 0); candidates[0][0] = candidates[0][k_]; } offsets[keep] = 1; } } } /** Determine top t values given a procedure to order k values. */ // ToDo: define *determine*; currently: Leave in decreasing priority // in candidates[t_] // currently for t < k; k =(<) t gets (much) more complicated, still // main idea: comparing values known to be smaller than t-1 values, // only one needs to be kept; <t-2: 2, ... // compare values of same "potential rank" up to some wrap-up. public void topByOrderK(final E[] many) { final int n = many.length; if (n < t) throw new IllegalArgumentException("number of values supplied" + " may not be less than number of result values required."); // keep up to k values of each potential rank ... candidates = (E[][]) new Object[t][]; // java.lang.reflect. // Array.newInstance(many.getClass(), t); for (int ci = candidates.length ; 0 <= --ci ; ) candidates[ci] = Arrays.copyOf(many, k); if (n <= k) { // one application of orderK should suffice handleFew(many); return; } // ... and next free offset offsets = new int[t]; candidates[t_][k_] = many[0]; // arraycopy in loop will fill 0:k_ int offset = 1; // used in wrap-up for ( ; offset <= n - k ; offset += k_) { System.arraycopy(many, offset, candidates[t_], 0, k_); keep(t, k); } for (int left = n - offset ; 0 < left ; left = 0) { candidates[t_][0] = candidates[t_][k_]; System.arraycopy(many, offset, candidates[t_], 1, left); offsets[t_] = 1 + left; } for (int rank = t, top = Integer.MIN_VALUE ; 0 <= --rank ; ) { switch(offsets[rank]) { case 0: // shouldn't happen?! break; case 1: top = 0; break; default: Arrays.fill(candidates[rank], offsets[rank], k, nonTopT); keep(rank+1, offsets[rank]); top = k_; } candidates[t_][k_t+rank] = candidates[rank][top]; } // orderK(candidates[t_], 0); // ?? } /** place top t of up to k values in candidates[t_][0:t] */ private void handleFew(final E[] many) { final int n = many.length; if (n == t) return; if (n < k) { if (1 < t) { // fill up k values to order Arrays.fill(candidates[0], n, k, many[0]); orderK(candidates[0], 0); } System.arraycopy(many, 0, candidates[0], k - n, n); } orderK(candidates[0], 0); for (int p = 0 ; p <= k/2 ; p++) { // t_ might be 0 E tmp = candidates[0][k-p]; candidates[0][k-p] = candidates[t_][p]; candidates[t_][p] = tmp; } } public static void main(String[] args) { TopByOrderK<String> topper = new TopByOrderK<>(3, 5, null); String many[] = { "for", "int", "offset", "n", "k", "order", "apply", "many", // "runnersUp", "top", "candidates", // "offsets", "trickle", "down", "x", "z" }; topper.topByOrderK(many); System.out.println(Arrays.asList(topper.candidates[topper.t_]) .subList(topper.k_t, topper.k)); System.out.println(topper.calls + " calls"); } }
class [[Ljava.lang.Object; cannot be cast to class [[Ljava.lang.String; ([[Ljava.lang.Object; and [[Ljava.lang.String; are in module java.base of loader 'bootstrap')? \$\endgroup\$candidatesas a capacity limited speciality priority queue.) \$\endgroup\$