Per ragioni pratiche e valori ragionevoli di n
si sta meglio di un anello-buffer dei primitivi int
s (per tenere traccia dei dati più vecchi), e una scansione lineare per determinare quanti valori sono più piccoli di x
.
Per poter essere in O(log n)
, è necessario utilizzare qualcosa come Guavas TreeMultiset. Ecco una descrizione di come sarebbe.
class Statistics {
private final static int N = 180;
Queue<Integer> queue = new LinkedList<Integer>();
SortedMap<Integer, Integer> counts = new TreeMap<Integer, Integer>();
public int insertAndGetSmallerCount(int x) {
queue.add(x); // O(1)
counts.put(x, getCount(x) + 1); // O(log N)
int lessCount = 0; // O(N), unfortunately
for (int i : counts.headMap(x).values()) // use Guavas TreeMultiset
lessCount += i; // for O(log n)
if (queue.size() > N) { // O(1)
int oldest = queue.remove(); // O(1)
int newCount = getCount(oldest) - 1; // O(log N)
if (newCount == 0)
counts.remove(oldest); // O(log N)
else
counts.put(oldest, newCount); // O(log N)
}
return lessCount;
}
private int getCount(int x) {
return counts.containsKey(x) ? counts.get(x) : 0;
}
}
Sul mio 1.Computer portatile da 8 GHz, questa soluzione esegue 1.000.000 iterazioni su circa 13 secondi (vale a dire che un'iterazione richiede circa 0,013 ms, ben al di sotto dei 100 ms).
Dato che ci sono solo 180 numeri e il ricalcolo si verifica solo ogni 100 ms, io sicuramente ottimizzo per la leggibilità e non per la velocità. – CodesInChaos
+1: quasi la stessa soluzione che ho ottenuto. –
@CodeInChaos, non penso che sarebbe più leggibile con una lista. Inoltre, chi dice che 180 è scolpito nella pietra? ;) – aioobe