Trova il numero intero più piccolo non in un elenco


87

Una domanda da intervista interessante che utilizza un mio collega:

Supponiamo che ti venga fornito un elenco molto lungo e non ordinato di interi a 64 bit senza segno. Come troveresti il ​​più piccolo numero intero non negativo che non compare nell'elenco?

FOLLOW-UP: Ora che è stata proposta l'ovvia soluzione mediante l'ordinamento, puoi farlo più velocemente di O (n log n)?

FOLLOW-UP: il tuo algoritmo deve essere eseguito su un computer con, diciamo, 1 GB di memoria

CHIARIMENTO: l'elenco è nella RAM, anche se potrebbe consumarne una grande quantità. Ti viene data la dimensione dell'elenco, diciamo N, in anticipo.


6
Penso che tu possa tralasciare la parte non negativa, visto che stai parlando di un numero intero senza segno.
KevenDenen

4
La domanda è piuttosto semplice, a meno che non sia mooolto fuori base, IMO, ma, come altri hanno già detto, ci sono domande da porre o ipotesi che dovrebbero essere dichiarate.
James Black

8
@paxdiablo: questo è un caso in cui dire O (n) non significa molto. Anche se memorizzi il tuo array a 2 ^ 64 bit su tavolette di argilla sull'Isola di Pasqua e vi si accede da piccione viaggiatore, l'algoritmo è ancora O (n).
IJ Kennedy

6
Cambiare i requisiti di memoria a metà rende questa una grande domanda per l'intervista ;-)
Chris Ballance

1
Penso che sia divertente che tutte le risposte facciano la stessa soluzione generale (ordina l'array e trova il primo valore che interrompe la sequenza), ma usano tutte un ordinamento diverso. (Quicksort modificato, radix sort, ...) La risposta accettata è equivalente a un ordinamento conteggio che scarta gli elementi sopra N.
Joren

Risposte:


121

Se la struttura dei dati può essere modificata in posizione e supporta l'accesso casuale, puoi farlo in O (N) tempo e O (1) spazio aggiuntivo. Basta scorrere l'array in modo sequenziale e per ogni indice scrivere il valore nell'indice nell'indice specificato da value, posizionando ricorsivamente qualsiasi valore in quella posizione al suo posto e buttando via i valori> N. Quindi ripassare l'array cercando il punto dove il valore non corrisponde all'indice: è il valore più piccolo non nell'array. Ciò si traduce in al massimo 3N confronti e utilizza solo pochi valori di spazio temporaneo.

# Pass 1, move every value to the position of its value
for cursor in range(N):
    target = array[cursor]
    while target < N and target != array[target]:
        new_target = array[target]
        array[target] = target
        target = new_target

# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
    if array[cursor] != cursor:
        return cursor
return N

9
Piccolo pignolo. Ti sei perso un caso banale: quando l'elenco è {0, ..., N-1}. In quel caso il passaggio 1 non fa nulla e nel passaggio 2 array [cursore] == cursore per tutte le voci nell'elenco, quindi l'algoritmo non ritorna. Quindi hai bisogno di un'istruzione "return N" alla fine.
Alex

12
La tua soluzione unisce il dominio e l'intervallo (l'obiettivo è sia un valore che un indice). L'intervallo è limitato dalla memoria disponibile a 128 milioni di elementi, ma il dominio ha una dimensione di 2G. Non riuscirà con una singola voce con un valore maggiore del numero di voci che possono essere allocate nell'array. Se la domanda non specificava "molto lungo", la risposta è elegante, anche se distrugge l'input. Il compromesso tempo-spazio è molto evidente in questo problema e una soluzione O (N) potrebbe non essere possibile in base ai vincoli forniti.
Pekka

2
Il secondo passaggio potrebbe utilizzare la ricerca binaria invece della ricerca lineare.
user448810

4
Questa soluzione funziona solo se l'intervallo di valori e l'indice sono confrontabili.
Dubby

7
Funzionerà bene con valori più grandi. I valori più grandi possono essere ignorati perché non possono avere nulla a che fare con il valore più piccolo non presente nell'array. Per il tuo esempio, il primo passaggio eseguirà un ciclo sull'array ignorando tutti i valori a causa di target <N e quindi restituirà 0 alla prima iterazione del secondo passaggio.
Ants Aasma

89

Ecco una semplice O(N)soluzione che utilizza lo O(N)spazio. Presumo che stiamo limitando l'elenco di input a numeri non negativi e che vogliamo trovare il primo numero non negativo che non è nell'elenco.

  1. Trova la lunghezza dell'elenco; diciamo che lo è N.
  2. Alloca un array di Nbooleani, inizializzato a tutti false.
  3. Per ogni numero Xnell'elenco, se Xè minore di N, imposta l' X'thelemento della matrice su true.
  4. Scansiona l'array partendo da index 0, cercando il primo elemento che è false. Se trovi il primo falsein index I, allora Iè la risposta. Altrimenti (cioè quando tutti gli elementi sono true) la risposta è N.

In pratica, l '"array di Nbooleani" verrebbe probabilmente codificato come "bitmap" o "bitset" rappresentato come un array byteo int. Questo in genere utilizza meno spazio (a seconda del linguaggio di programmazione) e consente di eseguire falsepiù rapidamente la scansione del primo .


Ecco come / perché funziona l'algoritmo.

Supponiamo che i Nnumeri nell'elenco non siano distinti o che uno o più di essi sia maggiore di N. Ciò significa che deve esserci almeno un numero nell'intervallo 0 .. N - 1che non è nell'elenco. Quindi il problema di trovare il numero mancante più piccolo deve quindi ridursi al problema di trovare il numero mancante più piccolo minore diN . Ciò significa che non è necessario tenere traccia dei numeri maggiori o uguali a N... perché non saranno la risposta.

L'alternativa al paragrafo precedente è che l'elenco è una permutazione dei numeri da 0 .. N - 1. In questo caso, il passaggio 3 imposta tutti gli elementi dell'array su truee il passaggio 4 ci dice che il primo numero "mancante" è N.


La complessità computazionale dell'algoritmo è O(N)con una costante di proporzionalità relativamente piccola. Esegue due passaggi lineari nell'elenco, o solo un passaggio se si sa che la lunghezza dell'elenco inizia con. Non è necessario rappresentare l'intero elenco in memoria, quindi l'utilizzo asintotico della memoria dell'algoritmo è proprio ciò che è necessario per rappresentare l'array di booleani; cioè O(N)bit.

(Al contrario, gli algoritmi che si basano sull'ordinamento o sul partizionamento in memoria presumono che tu possa rappresentare l'intero elenco in memoria. Nella forma in cui è stata posta la domanda, ciò richiederebbe O(N)parole a 64 bit.)


@Jorn commenta che i passaggi da 1 a 3 sono una variazione del conteggio dell'ordinamento. In un certo senso ha ragione, ma le differenze sono significative:

  • Un ordinamento conteggio richiede una matrice di (almeno) Xmax - Xmincontatori in cui Xmaxè il numero più grande nell'elenco ed Xminè il numero più piccolo nell'elenco. Ogni contatore deve essere in grado di rappresentare N stati; cioè assumendo una rappresentazione binaria deve avere un tipo intero (almeno) ceiling(log2(N))bit.
  • Per determinare la dimensione della matrice, un ordinamento conteggio deve eseguire un passaggio iniziale nell'elenco per determinare Xmaxe Xmin.
  • Il requisito di spazio minimo nel caso peggiore è quindi ceiling(log2(N)) * (Xmax - Xmin)bit.

Al contrario, l'algoritmo presentato sopra richiede semplicemente dei Nbit nei casi peggiori e migliori.

Tuttavia, questa analisi porta all'intuizione che se l'algoritmo eseguisse un passaggio iniziale attraverso la lista cercando uno zero (e contando gli elementi della lista se necessario), darebbe una risposta più rapida senza usare alcuno spazio se trovasse lo zero. Vale sicuramente la pena farlo se c'è un'alta probabilità di trovare almeno uno zero nell'elenco. E questo passaggio extra non cambia la complessità generale.


EDIT: Ho cambiato la descrizione dell'algoritmo per utilizzare "array di booleani" poiché le persone apparentemente hanno trovato la mia descrizione originale utilizzando bit e bitmap per creare confusione.


3
@ adi92 Se il passaggio 3 fornisce una bitmap con tutti i bit impostati su 1, l'elenco contiene tutti i valori da 0 a N-1. Ciò significa che il più piccolo numero intero non negativo nell'elenco è N. Se c'è un valore tra 0 e N-1 che NON è nell'elenco, il bit corrispondente non verrà impostato. Il valore più piccolo è quindi la risposta.
divegeek

4
@ adi92 Nel tuo esempio, l'elenco conterrebbe 300 elementi. Ciò significa che se c'è un valore "mancante", deve essere inferiore a 300. Eseguendo l'algoritmo, creeremmo un campo di bit con 300 slot, quindi imposteremo ripetutamente i bit negli slot 1, 2 e 3, lasciando tutti gli altri slot, da 0 e da 4 a 299, sono liberi. Durante la scansione del campo di bit troveremmo il flag nello slot 0 chiaro, quindi sapremmo che 0 è la risposta.
divegeek

4
Si noti che questo algoritmo potrebbe essere compreso in modo più semplice senza trucchi: "Crea un array booleano di dimensione N" ecc. Una volta capito in questo modo, passare a una versione bit per bit è concettualmente facile.
Jon Skeet

2
Quando si fornisce una soluzione astratta, utilizzare il modo concettualmente più semplice che funzioni e non specializzarsi eccessivamente. La tua soluzione urla per l'uso di un array booleano (astratto), quindi chiamalo così. Il fatto che tu possa implementare questo array tramite bool[]o tramite una bitmap è irrilevante per la soluzione generale.
Joren

2
Penso che questa soluzione possa essere meglio descritta da "Usa un ordinamento conteggio che ignora gli elementi sopra N, quindi trova il primo elemento mancante eseguendo una ricerca lineare dall'inizio".
Joren

13

Dato che l'OP ha ora specificato che l'elenco originale è contenuto nella RAM e che il computer ha solo, diciamo, 1 GB di memoria, esco su un arto e prevedo che la risposta è zero.

1 GB di RAM significa che l'elenco può contenere al massimo 134.217.728 numeri. Ma ci sono 2 64 = 18.446.744.073.709.551.616 numeri possibili. Quindi la probabilità che zero sia nella lista è 1 su 137.438.953.472.

Al contrario, le mie probabilità di essere colpito da un fulmine quest'anno sono 1 su 700.000. E le mie probabilità di essere colpito da un meteorite sono di circa 1 su 10 trilioni. Quindi ho circa dieci volte più probabilità di essere scritto su una rivista scientifica a causa della mia morte prematura da un oggetto celeste rispetto alla risposta che non è zero.


11
Il calcolo vale solo se i valori sono distribuiti uniformemente e selezionati a caso. Potrebbero anche essere stati generati in sequenza.
divegeek

1
Hai ragione, ovviamente. Ma sto solo ottimizzando per il caso comune. :)
Barry Brown

10
Quindi, quali sono le probabilità che l'intervistato venga selezionato con questa risposta?
Amarghosh

6
La domanda non dice che i numeri sono selezionati in modo uniforme e casuale. Sono selezionati dalla persona che pone questa domanda. Detto questo, la probabilità che 0 sia nell'elenco è molto maggiore di 1 su 137,438,953,472, probabilmente anche maggiore di 1 su 2. :-)
ShreevatsaR

8
@Amarghosh Anche la risposta a questa domanda è zero.
PeterAllenWebb

10

Come sottolineato in altre risposte, puoi eseguire un ordinamento e quindi eseguire semplicemente la scansione fino a trovare uno spazio vuoto.

È possibile migliorare la complessità algoritmica in O (N) e mantenere lo spazio O (N) utilizzando un QuickSort modificato in cui si eliminano le partizioni che non sono potenziali candidati per contenere il divario.

  • Nella prima fase di partizione, rimuovere i duplicati.
  • Una volta completato il partizionamento, controlla il numero di elementi nella partizione inferiore
  • Questo valore è uguale al valore utilizzato per creare la partizione?
    • Se è così, allora significa che il divario è nella partizione più alta.
      • Continua con il quicksort, ignorando la partizione inferiore
    • Altrimenti lo spazio si trova nella partizione inferiore
      • Continua con il quicksort, ignorando la partizione più alta

Ciò consente di risparmiare un gran numero di calcoli.


È piuttosto carino. Si presume che sia possibile calcolare la lunghezza della partizione in un tempo inferiore al tempo lineare, cosa che può essere eseguita se viene memorizzata insieme all'array della partizione. Si presuppone inoltre che l'elenco originale sia contenuto nella RAM.
Barry Brown,

2
Se conosci la lunghezza dell'elenco, puoi anche escludere qualsiasi valore maggiore di len (elenco). Secondo il principio della casella, qualsiasi "buco" deve essere inferiore a len (elenco).
divegeek

1
Non penso sia O (n) ... Per prima cosa, non sono sicuro che tu possa rimuovere i duplicati fino a quando un elenco non è completamente ordinato. In secondo luogo, anche se puoi garantire l'eliminazione di metà dello spazio di ricerca a ogni iterazione (perché hai diviso in sotto e sopra il punto medio), hai ancora più passaggi (dipendenti da n) sui dati che dipendono da n.
paxdiablo

1
paxdiablo: puoi creare un nuovo elenco con solo valori univoci utilizzando un metodo bitmap come quello proposto da Stephen C. Questo viene eseguito in O (n) tempo e spazio. Non sono sicuro che possa essere fatto meglio di così.
Nic

9

Per illustrare una delle insidie ​​del O(N)pensiero, ecco un O(N)algoritmo che utilizza lo O(1)spazio.

for i in [0..2^64):
  if i not in list: return i

print "no 64-bit integers are missing"

1
Will ha ragione. Questo non è O (n) perché in realtà hai due loop qui, ma uno è implicito. Determinare se un valore è in una lista è un'operazione O (n) e lo stai facendo n volte nel tuo ciclo for. Questo lo rende O (n ^ 2).
Nic

6
Nic, Will, è O (n * N) dove n è la dimensione dell'elenco e N è la dimensione del dominio (numeri interi a 64 bit). Sebbene N sia un numero enorme, è ancora una costante, quindi formalmente la complessità del problema come affermato è O (n).
Ants Aasma

1
Formiche, sono d'accordo che è O (n N), ma N non è costante. Poiché l'algoritmo termina quando trova la risposta, il numero di iterazioni complete attraverso il ciclo esterno è uguale alla risposta, che a sua volta è vincolata dalla dimensione dell'elenco. Quindi, O (N n) è O (n ^ 2) in questo caso.
Will Harris,

12
Cercare un numero in un elenco di N elementi è chiaramente O (N). Lo facciamo 2 ^ 64 volte. Sebbene grande, 2 ^ 64 è una COSTANTE. Pertanto l'algoritmo è C * O (N), che è ancora O (N).
IJ Kennedy

3
Devo ritrattare la mia dichiarazione precedente; secondo la definizione più rigorosa, questa operazione è effettivamente O (n).
Nic

8

Poiché i numeri sono tutti lunghi 64 bit, possiamo usare l' ordinamento digitale su di essi, che è O (n). Ordinali, quindi scansionali finché non trovi quello che stai cercando.

se il numero più piccolo è zero, scansiona in avanti finché non trovi uno spazio vuoto. Se il numero più piccolo non è zero, la risposta è zero.


Vero, ma i requisiti di memoria potrebbero diventare piuttosto intensi per l'ordinamento digitale.
PeterAllenWebb

1
L'ordinamento digitale non funzionerà per set di dati molto grandi. Ma l'ordinamento della partizione e della radice potrebbe funzionare.
DarthVader

5

Per un metodo efficiente in termini di spazio e tutti i valori sono distinti, puoi farlo nello spazio O( k )e nel tempo O( k*log(N)*N ). È efficiente in termini di spazio e non ci sono dati in movimento e tutte le operazioni sono elementari (aggiunta di sottrazione).

  1. impostato U = N; L=0
  2. Prima partiziona lo spazio numerico in kregioni. Come questo:
    • 0->(1/k)*(U-L) + L, 0->(2/k)*(U-L) + L, 0->(3/k)*(U-L) + L...0->(U-L) + L
  3. Trova quanti numeri ( count{i}) ci sono in ogni regione. ( N*kpassaggi)
  4. Trova la prima regione ( h) che non è piena. Ciò significa count{h} < upper_limit{h}. ( kpassaggi)
  5. se h - count{h-1} = 1hai la tua risposta
  6. impostato U = count{h}; L = count{h-1}
  7. vai a 2

questo può essere migliorato usando l'hashing (grazie per Nic questa idea).

  1. stesso
  2. Prima partiziona lo spazio numerico in kregioni. Come questo:
    • L + (i/k)->L + (i+1/k)*(U-L)
  3. inc count{j} utilizzando j = (number - L)/k (if L < number < U)
  4. trova la prima regione ( h) che non contiene k elementi
  5. se count{h} = 1h è la tua risposta
  6. impostato U = maximum value in region h L = minimum value in region h

Questo verrà eseguito O(log(N)*N).


Mi piace molto questa risposta. È stato un po 'difficile da leggere, ma è molto simile a quello che avevo in testa quando ho letto la domanda.
Nic

inoltre a un certo punto sarebbe intelligente passare a quella soluzione bitmap di Stephen C. probabilmente quandoU-L < k
Egon

Questo non viene eseguito in O (log (N) * N) ma in O (N). La tua risposta è una generalizzazione della risposta di @cdiggins e viene eseguita in O (N) perché sum (1 / k ** i for i in range (ceil (log_k (n)))) <= 2.
Lapinot

Ad ogni iterazione che passi attraverso O (N) numeri, ci vogliono O (log_k (N)) iterazioni totali. Quindi O (log_k (N) * N) == O (log (N) * N). I numeri originali non sono ordinati / raggruppati e devi esaminarli tutti.
Egon

Ma se hai partizionato l'elenco originale in k regioni (di dimensione n / k), seleziona la prima regione che non è piena. Quindi nella prossima iterazione devi solo considerare la regione selezionata e dividerla in k nuove regioni (di dimensione n / k ** 2) ecc. In realtà non itererai su tutto l'elenco ogni volta (altrimenti qual'è il punto del partizionamento ?).
Lapinot

3

Li ordino, quindi eseguo la sequenza fino a trovare uno spazio vuoto (incluso lo spazio all'inizio tra zero e il primo numero).

In termini di algoritmo, qualcosa del genere lo farebbe:

def smallest_not_in_list(list):
    sort(list)
    if list[0] != 0:
        return 0
    for i = 1 to list.last:
        if list[i] != list[i-1] + 1:
            return list[i-1] + 1
    if list[list.last] == 2^64 - 1:
        assert ("No gaps")
    return list[list.last] + 1

Ovviamente, se hai molta più memoria del grugnito della CPU, potresti creare una maschera di bit di tutti i possibili valori a 64 bit e impostare semplicemente i bit per ogni numero nell'elenco. Quindi cerca il primo bit 0 in quella maschera di bit. Questo la trasforma in un'operazione O (n) in termini di tempo ma piuttosto dannatamente costosa in termini di requisiti di memoria :-)

Dubito che potresti migliorare su O (n) poiché non riesco a vedere un modo per farlo che non implichi guardare ogni numero almeno una volta.

L'algoritmo per quello sarebbe sulla falsariga di:

def smallest_not_in_list(list):
    bitmask = mask_make(2^64) // might take a while :-)
    mask_clear_all (bitmask)
    for i = 1 to list.last:
        mask_set (bitmask, list[i])
    for i = 0 to 2^64 - 1:
        if mask_is_clear (bitmask, i):
            return i
    assert ("No gaps")

Dalla descrizione sembra precludere 0 al primo elemento, in quanto è il più piccolo non in elenco. Ma questa è un'ipotesi che ho fatto, potrei sbagliarmi.
James Black

I miei pensieri erano che se la sequenza ordinata fosse 4,5,6, allora 0 sarebbe il più piccolo non nell'elenco.
paxdiablo

Mi aspetto che 2, 3, 5, la risposta dovrebbe essere 4, ma potrei sbagliarmi.
James Black

Una domanda a cui dovrebbe rispondere l'OP. Lo spazio di ricerca è "tutti i numeri interi senza segno a 64 bit" o "tutti i numeri tra il più basso e il più alto nell'elenco"?
paxdiablo

Sono d'accordo sul fatto che nel peggiore dei casi devi guardare almeno una volta, a meno che non sia già stato ordinato in un albero binario, forse.
James Black

2

Ordina l'elenco, guarda il primo e il secondo elemento e inizia a salire finché non c'è uno spazio vuoto.


Dipende da come lo definisci, non nell'elenco.
James Black

@PeterAllenWebb - Ci saranno, ma i numeri sono in ordine casuale o ordinati?
James Black

1

Puoi farlo in O (n) tempo e O (1) spazio aggiuntivo, sebbene il fattore nascosto sia piuttosto grande. Questo non è un modo pratico per risolvere il problema, ma potrebbe comunque essere interessante.

Per ogni numero intero a 64 bit senza segno (in ordine crescente), iterare l'elenco fino a trovare il numero intero di destinazione o alla fine dell'elenco. Se raggiungi la fine dell'elenco, il numero intero di destinazione è il numero intero più piccolo non nell'elenco. Se raggiungi la fine degli interi a 64 bit, ogni intero a 64 bit è nell'elenco.

Eccola come una funzione Python:

def smallest_missing_uint64(source_list):
    the_answer = None

    target = 0L
    while target < 2L**64:

        target_found = False
        for item in source_list:
            if item == target:
                target_found = True

        if not target_found and the_answer is None:
            the_answer = target

        target += 1L

    return the_answer

Questa funzione è deliberatamente inefficiente per mantenerla O (n). Nota in particolare che la funzione continua a controllare gli interi di destinazione anche dopo che la risposta è stata trovata. Se la funzione viene restituita non appena viene trovata la risposta, il numero di volte in cui è stato eseguito il ciclo esterno sarebbe limitato dalla dimensione della risposta, che è limitata da n. Questa modifica renderebbe il tempo di esecuzione O (n ^ 2), anche se sarebbe molto più veloce.


Vero. È divertente come orribilmente alcuni degli algoritmi che sono O (1) spazio e O (n) tempo falliscano in pratica con questa domanda.
PeterAllenWebb

1

Grazie a egon, swilden e Stephen C per la mia ispirazione. Innanzitutto, conosciamo i limiti del valore dell'obiettivo perché non può essere maggiore della dimensione dell'elenco. Inoltre, un elenco da 1 GB potrebbe contenere al massimo 134217728 (128 * 2 ^ 20) numeri interi a 64 bit.

Parte di hashing
che propongo di utilizzare l'hashing per ridurre drasticamente il nostro spazio di ricerca. Innanzitutto, radice quadrata della dimensione dell'elenco. Per un elenco da 1 GB, è N = 11.586. Imposta un array intero di dimensione N. Scorri l'elenco e prendi la radice quadrata * di ogni numero che trovi come hash. Nella tua tabella hash, incrementa il contatore per quell'hash. Quindi, itera attraverso la tua tabella hash. Il primo bucket che trovi che non è uguale alla sua dimensione massima definisce il tuo nuovo spazio di ricerca.

Parte bitmap
Ora imposta una bitmap regolare uguale alla dimensione del tuo nuovo spazio di ricerca, e di nuovo itera attraverso l'elenco sorgente, compilando la bitmap ogni volta che trovi ogni numero nel tuo spazio di ricerca. Quando hai finito, il primo bit non impostato nella tua bitmap ti darà la tua risposta.

Questo sarà completato nel tempo O (n) e nello spazio O (sqrt (n)).

(* Potresti usare qualcosa come il bit shifting per farlo in modo molto più efficiente e semplicemente variare il numero e la dimensione dei bucket di conseguenza.)


1
Mi piace l'idea di dividere lo spazio di ricerca in bucket Root-N per ridurre il footprint di memoria, ma i duplicati nell'elenco rompono questo metodo. Mi chiedo se possa essere risolto.
PeterAllenWebb

Hai ragione, ho trascurato di considerare le voci duplicate. Non sono sicuro che possa essere aggirato.
Nic

1

Bene, se c'è un solo numero mancante in un elenco di numeri, il modo più semplice per trovare il numero mancante è sommare le serie e sottrarre ogni valore nell'elenco. Il valore finale è il numero mancante.


Si. Questa è un'altra classica domanda da intervista.
PeterAllenWebb

1
Ancora più facile di così è XOR i numeri nell'elenco insieme, XOR i numeri nell'intervallo insieme e XOR insieme i risultati.
John Kurlak

1
 int i = 0;
            while ( i < Array.Length)
            {

                if (Array[i] == i + 1)
                {
                    i++;
                }

                if (i < Array.Length)
                {
                    if (Array[i] <= Array.Length)
                    {//SWap

                        int temp = Array[i];
                        int AnoTemp = Array[temp - 1];
                        Array[temp - 1] = temp;
                        Array[i] = AnoTemp;

                    }
                    else
                       i++;



                }
            }

            for (int j = 0; j < Array.Length; j++)
            {
                if (Array[j] > Array.Length)
                {
                    Console.WriteLine(j + 1);
                    j = Array.Length;
                }
                else
                    if (j == Array.Length - 1)
                        Console.WriteLine("Not Found !!");

            }
        }

1

Potremmo usare una tabella hash per contenere i numeri. Una volta completati tutti i numeri, eseguire un contatore da 0 fino a trovare il più basso. Un hash ragionevolmente buono si hash e si memorizza a tempo costante e si recupera in tempo costante.

for every i in X         // One scan Θ(1)
   hashtable.put(i, i);  // O(1)

low = 0;

while (hashtable.get(i) <> null)   // at most n+1 times
   low++;

print low;

Il caso peggiore se ci sono nelementi nell'array e sono {0, 1, ... n-1}, nel qual caso, la risposta sarà ottenuta n, mantenendola ancora O(n).


1

Ecco la mia risposta scritta in Java:

Idea di base: 1- Esegui il ciclo attraverso la matrice eliminando i numeri positivi, zero e negativi duplicati mentre riassumi il resto, ottenendo anche il numero massimo positivo e mantieni i numeri positivi univoci in una mappa.

2- Calcola la somma come max * (max + 1) / 2.

3- Trova la differenza tra le somme calcolate nei passaggi 1 e 2

4- Ripeti il ​​ciclo da 1 al minimo di [somma differenza, max] e restituisci il primo numero che non è nella mappa popolata al punto 1.

public static int solution(int[] A) {
    if (A == null || A.length == 0) {
        throw new IllegalArgumentException();
    }

    int sum = 0;
    Map<Integer, Boolean> uniqueNumbers = new HashMap<Integer, Boolean>();
    int max = A[0];
    for (int i = 0; i < A.length; i++) {
        if(A[i] < 0) {
            continue;
        }
        if(uniqueNumbers.get(A[i]) != null) {
            continue;
        }
        if (A[i] > max) {
            max = A[i];
        }
        uniqueNumbers.put(A[i], true);
        sum += A[i];
    }
    int completeSum = (max * (max + 1)) /  2;
    for(int j = 1; j <= Math.min((completeSum - sum), max); j++) {
        if(uniqueNumbers.get(j) == null) { //O(1)
            return j;
        }
    }
    //All negative case
    if(uniqueNumbers.isEmpty()) {
        return 1;
    }
    return 0;
}

0

Come ha sottolineato elegantemente Stephen C, la risposta deve essere un numero inferiore alla lunghezza dell'array. Quindi troverei la risposta tramite la ricerca binaria. Questo ottimizza il caso peggiore (quindi l'intervistatore non può coglierti in uno scenario patologico "what if"). In un'intervista, fai notare che lo stai facendo per ottimizzare per il caso peggiore.

Il modo per utilizzare la ricerca binaria è sottrarre il numero che stai cercando da ogni elemento dell'array e controllare i risultati negativi.


0

Mi piace l'approccio "indovina zero". Se i numeri fossero casuali, zero è altamente probabile. Se l '"esaminatore" ha impostato un elenco non casuale, aggiungine uno e indovina di nuovo:

LowNum=0
i=0
do forever {
  if i == N then leave /* Processed entire array */
  if array[i] == LowNum {
     LowNum++
     i=0
     }
   else {
     i++
   }
}
display LowNum

Il caso peggiore è n * N con n = N, ma in pratica è molto probabile che n sia un numero piccolo (es. 1)


0

Non sono sicuro di aver ricevuto la domanda. Ma se per la lista 1,2,3,5,6 e il numero mancante è 4, allora il numero mancante può essere trovato in O (n) da: (n + 2) (n + 1) / 2- (n + 1) n / 2

EDIT: scusa, immagino di aver pensato troppo in fretta la scorsa notte. Ad ogni modo, la seconda parte dovrebbe essere effettivamente sostituita da sum (list), che è dove viene O (n). La formula rivela l'idea alla base: per n interi sequenziali, la somma dovrebbe essere (n + 1) * n / 2. Se c'è un numero mancante, la somma sarebbe uguale alla somma di (n + 1) numeri interi sequenziali meno il numero mancante.

Grazie per aver sottolineato il fatto che stavo mettendo alcuni pezzi intermedi nella mia mente.


1
A prima vista non vedo come funzionerebbe. Nel tuo caso n = 5 e la formula sarà fissata, indipendentemente dal numero che mancava.
sisve

Simon: potresti ora rimuovere il voto negativo in base alla mia modifica?
Codismo

0

Ben fatto Ants Aasma! Ho pensato alla risposta per circa 15 minuti e in modo indipendente ho trovato una risposta in modo simile alla tua:

#define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
    int m = n;
    for (int i = 0; i < m;) {
        if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
            m--;
            SWAP (a[i], a[m]);
            continue;
        }
        if (a[i] > i) {
            SWAP (a[i], a[a[i]]);
            continue;
        }
        i++;
    }
    return m;
}

m rappresenta "l'attuale output massimo possibile dato quello che so sui primi i input e non assumendo nient'altro sui valori fino all'entrata in m-1".

Questo valore di m verrà restituito solo se (a [i], ..., a [m-1]) è una permutazione dei valori (i, ..., m-1). Quindi se a [i]> = mo se a [i] <i o se a [i] == a [a [i]] sappiamo che m è l'uscita sbagliata e deve essere almeno un elemento inferiore. Quindi diminuendo m e scambiando a [i] con a [m] possiamo ricorrere.

Se questo non è vero ma a [i]> i allora sapendo che a [i]! = A [a [i]] sappiamo che scambiare a [i] con a [a [i]] aumenterà il numero di elementi al loro posto.

Altrimenti a [i] deve essere uguale a i, nel qual caso possiamo incrementare i sapendo che tutti i valori fino a questo indice incluso sono uguali al loro indice.

La prova che questo non può entrare in un ciclo infinito è lasciata come esercizio al lettore. :)


0

Il frammento Dafny della risposta di Ants mostra perché l'algoritmo sul posto potrebbe non riuscire. La requirescondizione preliminare descrive che i valori di ogni elemento non devono andare oltre i limiti dell'array.

method AntsAasma(A: array<int>) returns (M: int)
  requires A != null && forall N :: 0 <= N < A.Length ==> 0 <= A[N] < A.Length;
  modifies A; 
{
  // Pass 1, move every value to the position of its value
  var N := A.Length;
  var cursor := 0;
  while (cursor < N)
  {
    var target := A[cursor];
    while (0 <= target < N && target != A[target])
    {
        var new_target := A[target];
        A[target] := target;
        target := new_target;
    }
    cursor := cursor + 1;
  }

  // Pass 2, find first location where the index doesn't match the value
  cursor := 0;
  while (cursor < N)
  {
    if (A[cursor] != cursor)
    {
      return cursor;
    }
    cursor := cursor + 1;
  }
  return N;
}

Incolla il codice nel validatore con e senza la forall ...clausola per vedere l'errore di verifica. Il secondo errore è il risultato dell'impossibilità da parte del verificatore di stabilire una condizione di terminazione per il ciclo Pass 1. Dimostrare che questo è lasciato a qualcuno che capisce meglio lo strumento.


0

Ecco una risposta in Java che non modifica l'input e utilizza tempo O (N) e N bit più un piccolo overhead costante di memoria (dove N è la dimensione dell'elenco):

int smallestMissingValue(List<Integer> values) {
    BitSet bitset = new BitSet(values.size() + 1);
    for (int i : values) {
        if (i >= 0 && i <= values.size()) {
            bitset.set(i);
        }
    }
    return bitset.nextClearBit(0);
}

0
def solution(A):

index = 0
target = []
A = [x for x in A if x >=0]

if len(A) ==0:
    return 1

maxi = max(A)
if maxi <= len(A):
    maxi = len(A)

target = ['X' for x in range(maxi+1)]
for number in A:
    target[number]= number

count = 1
while count < maxi+1:
    if target[count] == 'X':
        return count
    count +=1
return target[count-1] + 1

Ottenuto il 100% per la soluzione di cui sopra.


0

1) Filtro negativo e zero

2) Ordina / distinto

3) Visita array

Complessità : O (N) o O (N * log (N))

utilizzando Java8

public int solution(int[] A) {
            int result = 1;
    boolean found = false;
    A = Arrays.stream(A).filter(x -> x > 0).sorted().distinct().toArray();
    //System.out.println(Arrays.toString(A));
    for (int i = 0; i < A.length; i++) {
        result = i + 1;
        if (result != A[i]) {
            found = true;
            break;
        }
    }
    if (!found && result == A.length) {
        //result is larger than max element in array
        result++;
    }
    return result;
}

0

Un unordered_set può essere utilizzato per memorizzare tutti i numeri positivi, quindi possiamo iterare da 1 alla lunghezza di unordered_set e vedere il primo numero che non si verifica.

int firstMissingPositive(vector<int>& nums) {

    unordered_set<int> fre;
    // storing each positive number in a hash.
    for(int i = 0; i < nums.size(); i +=1)
    {
        if(nums[i] > 0)
            fre.insert(nums[i]);
     }

    int i = 1;
    // Iterating from 1 to size of the set and checking 
    // for the occurrence of 'i'

    for(auto it = fre.begin(); it != fre.end(); ++it)
    {
        if(fre.find(i) == fre.end())
            return i;
        i +=1;
    }

    return i;
}

0

Soluzione tramite javascript di base

var a = [1, 3, 6, 4, 1, 2];

function findSmallest(a) {
var m = 0;
  for(i=1;i<=a.length;i++) {
    j=0;m=1;
    while(j < a.length) {
      if(i === a[j]) {
        m++;
      }
      j++;
    }
    if(m === 1) {
      return i;
    }
  }
}

console.log(findSmallest(a))

Spero che questo aiuti per qualcuno.


0

Con python non è il più efficiente, ma corretto

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import datetime

# write your code in Python 3.6

def solution(A):
    MIN = 0
    MAX = 1000000
    possible_results = range(MIN, MAX)

    for i in possible_results:
        next_value = (i + 1)
        if next_value not in A:
            return next_value
    return 1

test_case_0 = [2, 2, 2]
test_case_1 = [1, 3, 44, 55, 6, 0, 3, 8]
test_case_2 = [-1, -22]
test_case_3 = [x for x in range(-10000, 10000)]
test_case_4 = [x for x in range(0, 100)] + [x for x in range(102, 200)]
test_case_5 = [4, 5, 6]
print("---")
a = datetime.datetime.now()
print(solution(test_case_0))
print(solution(test_case_1))
print(solution(test_case_2))
print(solution(test_case_3))
print(solution(test_case_4))
print(solution(test_case_5))

0
def solution(A):
    A.sort()
    j = 1
    for i, elem in enumerate(A):
        if j < elem:
            break
        elif j == elem:
            j += 1
            continue
        else:
            continue
    return j

0

questo può aiutare:

0- A is [5, 3, 2, 7];
1- Define B With Length = A.Length;                            (O(1))
2- initialize B Cells With 1;                                  (O(n))
3- For Each Item In A:
        if (B.Length <= item) then B[Item] = -1                (O(n))
4- The answer is smallest index in B such that B[index] != -1  (O(n))

Questo differisce dalla risposta di Stephen C ? Come?
barba grigia
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.