Frequenza delle parole con ordinamento nella complessità O (n)


11

Durante un'intervista per una posizione di sviluppatore Java, mi è stato chiesto quanto segue:

Scrivi una funzione che accetta due parametri:

  1. una stringa che rappresenta un documento di testo e
  2. un numero intero che fornisce il numero di articoli da restituire.

Implementare la funzione in modo tale da restituire un elenco di stringhe ordinate per frequenza delle parole, la parola che si verifica più frequentemente per prima. La soluzione dovrebbe essere eseguita nel tempo cui è il numero di caratteri nel documento.nO(n)n

Quello che segue è quello che ho risposto (in pseudocodice), non è ma piuttosto tempo a causa del tipo. Non riesco a capire come farlo volta. O ( n log n ) O ( n )O(n)O(nlogn)O(n)

wordFrequencyMap = new HashMap<String, Integer>();
words = inputString.split(' ');

for (String word : words) {
  count = wordFrequencyMap.get(word);
  count = (count == null) ? 1 : ++count;
  wordFrequencyMap.put(word, count);
}

return wordFrequencyMap.sortByValue.keys

Qualcuno sa o qualcuno può darmi qualche suggerimento?


1
Usa una tabella hash.
Yuval Filmus,

L'uso di una tabella hash non risolve il problema. Inoltre, hashtable è Java legacy.
user2712937

Le tabelle hash sono in genere il trucco per ridurre la complessità da a . Anche se sono legacy Java, qualunque cosa significhi. Non ho verificato questo caso particolare, quindi potresti avere ragione. O ( n )O(nlogn)O(n)
Yuval Filmus,

@YuvalFilmus. Grazie, ma la tabella hash è praticamente la stessa della mappa hash, che sto già utilizzando (la principale differenza tra la struttura dei 2 dati è la sincronizzazione, che non si applica qui). Il log (n) nel mio deriva dall'ordinamento dei valori nella mappa hash.
user2712937

3
A proposito, questo sito si concentra su concetti e algoritmi, non sul codice. Pertanto, normalmente ti chiediamo di rimuovere il codice Java e di fornire una descrizione concettuale del tuo approccio (possibilmente con pseudocodice conciso di alto livello, se necessario). Inoltre, su questo sito la domanda rilevante è quali strutture di dati e algoritmi usare; l'API Java specifica è fuori tema per questo sito (ma è possibile chiedere informazioni su StackOverflow) e allo stesso modo, se HashtableJava legacy o meno è davvero irrilevante ai fini di questo sito.
DW

Risposte:


10

Suggerisco una variazione del conteggio delle distribuzioni:

  1. Leggi il testo e inserisci tutte le parole incontrate in un trie , mantenendo in ogni nodo un conteggio, con quale frequenza si è verificata la parola rappresentata da questo nodo. Inoltre tenere traccia del conteggio delle parole più alto dire maxWordCound. - O(n)
  2. Inizializza una matrice di dimensioni maxWordCount. Il tipo di voce è un elenco di stringhe. - , poiché il conteggio non può essere superiore.O(n)
  3. Attraversare il trie e per ogni nodo aggiungere la stringa corrispondente alla voce dell'array indicata dal conteggio. - , poiché la lunghezza totale delle stringhe è limitata da n .O(n)n
  4. Attraversa l'array in ordine decrescente e genera il numero desiderato di stringhe. - , poiché è legato sia alla dimensione che alla quantità di dati nell'array.O(n)

Probabilmente puoi sostituire il trie con altre strutture di dati nella prima fase.


+1, anche se non ne sono sicuro. È O (n) poiché il numero di parole da restituire è limitato da n, il numero di caratteri, ma è questa la domanda? O un risultato indipendente dal numero di parole restituite?
Nikos M.,

@NikosM. Lo è ; è un limite superiore nel caso peggiore in generale sul numero di parole restituite, non delle ipotesi necessarie. n
Raffaello

@Raphael, sì, ci sto pensando da quando è stato chiesto in un'intervista, possibili trucchi nella domanda ..
Nikos M.

Mi chiedo se esiste un algoritmo di tempo lineare efficiente in termini di spazio.
Saadtaame,

3
@saadtaame, sì, questa è una domanda interessante. Potrebbe valere la pena pubblicare separatamente come domanda separata. Non è solo efficienza dello spazio; la soluzione trie è anche ad alta intensità di puntatore, che potrebbe renderla più lenta in pratica (dato come funziona la gerarchia di memoria nelle macchine reali). L '"efficienza" è diversa dal tempo di esecuzione peggiore. Non è insolito che un algoritmo di tempo pulito superi un algoritmo di tempo O ( n ) ad alta intensità di puntatore , quindi questa domanda sembra già escludere alcuni potenziali algoritmi che potrebbero essere una scelta migliore nella pratica. O(nlgn)O(n)
DW

3

La raccolta dei conteggi di occorrenze è O (n), quindi il trucco sta davvero trovando solo i primi conteggi di occorrenze k.

Un heap è un modo comune per aggregare i principali valori k, sebbene possano essere utilizzati altri metodi (consultare https://en.wikipedia.org/wiki/Partial_sorting ).

Supponendo che k sia il secondo parametro sopra e che sia una costante nell'istruzione del problema (sembra essere):

  1. Crea un trie di parole con conteggi di occorrenze su ciascun nodo.
  2. Inizializza un heap di dimensioni k.
  3. Attraversa il trie e il min-probe / inserisci ogni coppia (foglia, conteggio delle occorrenze) nell'heap top-k.
  4. Stampa le prime foglie e conteggi di k (questo è in realtà un tipo di dolore perché hai bisogno di puntatori genitore per mappare ogni foglia su una parola).

Poiché la dimensione dell'heap è una costante, le operazioni dell'heap sono O (1), quindi il passaggio 3 è O (n).

L'heap potrebbe anche essere mantenuto dinamicamente durante la costruzione del trie.


2

Il tuo algoritmo non funziona nemmeno nel tempo ; inserire Θ ( n ) cose in un hashtable costa già tempo Ω ( n 2 ) (nel caso peggiore).O(nlogn)Θ(n)Ω(n2)


Quello che segue è sbagliato ; Lo lascio qui per il momento a scopo illustrativo.

O(n)Σn

  1. Costruisci un albero di suffissi del testo, ad esempio con l'algoritmo di Ukkonen .

    Se la costruzione non lo fa già, aggiungi il numero di foglie raggiungibili ad ogni nodo (interno).

  2. Attraversa l'albero dalla radice e taglia tutti i rami nel primo spazio (bianco).

  3. Attraversa l'albero e ordina l'elenco dei figli di ogni nodo in base al conteggio delle foglie.

  4. La resa dell'albero (foglie da sinistra a destra) è ora un elenco di tutte le parole, ordinate per frequenza.

Per quanto riguarda il runtime:

  1. O(n)Θ
  2. nn
  3. nO(|Σ|log|Σ|)=O(1)
  4. O(n)O(n)

È possibile ottenere limiti più precisi parametrizzando il tempo di esecuzione con il numero di parole diverse; se ce ne sono pochi, l'albero è piccolo dopo 2.


L'algoritmo non è corretto (non si ordina). Non sono più sicuro che il tempo lineare sia persino possibile.
Raffaello

1

HashMap1..nO(n)O(n)

O(n)O(n)O(n)

O(n)O(n)


Θ(n)Ω(n2)

Non posso parlare per gli intervistatori, ma sono riluttante a usare la loro sciattezza come scusa per più o meno lo stesso. Inoltre, questo sito riguarda la scienza (come tu stesso hai commentato sopra), non agitando a mano i trucchi di programmazione "come verrò pagato prima".
Raffaello

Fintanto che questa comprensione è resa esplicita, sto bene con quello. Ho visto troppe domande qui che sono state fondate nella confusione perché alcune implicite "comprensioni" hanno promosso idee sbagliate.
Raffaello

0

Soluzione basata su hash

Ω(n2)n

nΩ(n)

O(1)O(n)O(n2)n

Il presupposto è che l'algoritmo di hashing sia lineare nel tempo in relazione al numero di caratteri.

Soluzione basata sull'ordinamento Radix

O(kN)kNnkO(n)

2nnO(n)

Le prime poche parole più lunghe in inglese sono ridicolmente lunghe , ma poi si potrebbe limitare la lunghezza della parola a un numero ragionevole (come 30 o inferiore) e troncare le parole accettando il margine di errore che potrebbe derivarne.


Θ(n)Θ(n)

O(n+n)O(n2)

(3) Qualunque funzione hash tu scelga, posso trovare un input in cui quella specifica funzione degrada. E scegliere la funzione hash dopo aver saputo che l'input di solito non è un'opzione. (E ricorda che il commento a cui stavi presumibilmente rivolgendo riguardava il caso peggiore, non il caso tipico.)
FrankW

O(n2)

O(n2)O(1)Ω(1)O(1)O(1)
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.