Credo di aver trovato qualcosa che dovrebbe funzionare in modo generale ed efficiente se si è certi di non avere duplicati * (tuttavia, dovrebbe essere estensibile a qualsiasi numero di fori e qualsiasi intervallo di numeri interi).
L'idea alla base di questo metodo è come quicksort, in quanto troviamo un pivot e una partizione attorno ad esso, quindi recidiamo sul lato (i) con un buco. Per vedere quali lati hanno il foro, troviamo i numeri più bassi e più alti e confrontali con il perno e il numero di valori su quel lato. Supponiamo che il pivot sia 17 e che il numero minimo sia 11. Se non ci sono buchi, dovrebbero esserci 6 numeri (11, 12, 13, 14, 15, 16, 17). Se ci sono 5, sappiamo che c'è un buco su quel lato e possiamo ricorrere solo da quella parte per trovarlo. Ho difficoltà a spiegarlo in modo più chiaro, quindi prendiamo un esempio.
15 21 10 13 18 16 22 23 24 20 17 11 25 12 14
Pivot:
10 13 11 12 14 |15| 21 18 16 22 23 24 20 17 25
15 è il pivot, indicato da pipe ( ||
). Ci sono 5 numeri sul lato sinistro del perno, come dovrebbe essere (15 - 10) e 9 sulla destra, dove dovrebbero esserci 10 (25 - 15). Quindi recitiamo dalla parte giusta; noteremo che il limite precedente era 15 nel caso in cui il foro fosse adiacente ad esso (16).
[15] 18 16 17 20 |21| 22 23 24 25
Ora ci sono 4 numeri sul lato sinistro ma dovrebbero esserci 5 (21 - 16). Quindi ci rechiamo lì, e di nuovo noteremo il limite precedente (tra parentesi).
[15] 16 17 |18| 20 [21]
La parte sinistra ha i 2 numeri corretti (18 - 16), ma la destra ha 1 invece di 2 (20 - 18). A seconda delle condizioni finali, potremmo confrontare il numero 1 con i due lati (18, 20) e vedere che 19 manca o recita ancora una volta:
[18] |20| [21]
Il lato sinistro ha una dimensione pari a zero, con uno spazio tra il perno (20) e il limite precedente (18), quindi 19 è il foro.
*: Se ci sono duplicati, potresti probabilmente usare un hash set per rimuoverli in tempo O (N), mantenendo il metodo generale O (N), ma che potrebbe richiedere più tempo di usando un altro metodo.