A maneira mais eficiente de encontrar as K principais palavras frequentes em uma sequência de palavras grandes


85

Entrada: Um número inteiro positivo K e um texto grande. O texto pode realmente ser visto como uma sequência de palavras. Portanto, não precisamos nos preocupar em como quebrá-lo na sequência de palavras.
Saída: as K palavras mais frequentes no texto.

Meu pensamento é assim.

  1. use uma tabela de hash para registrar a frequência de todas as palavras enquanto percorre toda a sequência de palavras. Nesta fase, a chave é "palavra" e o valor é "frequência de palavras". Isso leva tempo O (n).

  2. classifique o par (palavra, palavra-frequência); e a chave é "frequência de palavras". Isso leva tempo O (n * lg (n)) com o algoritmo de classificação normal.

  3. Após a classificação, apenas pegamos as primeiras K palavras. Isso leva tempo O (K).

Para resumir, o tempo total é O (n + n lg (n) + K) , Uma vez que K é certamente menor que N, então é na verdade O (n lg (n)).

Podemos melhorar isso. Na verdade, queremos apenas as K palavras principais. A frequência de outras palavras não nos preocupa. Portanto, podemos usar "classificação Heap parcial". Para as etapas 2) e 3), não fazemos apenas a classificação. Em vez disso, mudamos para ser

2 ') construir um monte de pares de (palavra, palavra-frequência) com "palavra-frequência" como chave. Leva tempo O (n) para construir um heap;

3 ') extraia as K principais palavras da pilha. Cada extração é O (lg (n)). Portanto, o tempo total é O (k * lg (n)).

Para resumir, essa solução custa tempo O (n + k * lg (n)).

Este é apenas o meu pensamento. Não descobri uma maneira de melhorar a etapa 1).
Espero que alguns especialistas em Recuperação de Informações possam esclarecer melhor essa questão.


Você usaria classificação por mesclagem ou classificação rápida para classificação O (n * logn)?
Cometeu e

1
Para usos práticos, a resposta de Aaron Maenpaa de contar com uma amostra é a melhor. Não é como se as palavras mais frequentes se ocultassem da sua amostra. Para os geeks da complexidade, é O (1), pois o tamanho da amostra é fixo. Você não obtém as contagens exatas, mas também não as pede.
Nikana Reklawyks

Se você deseja uma revisão de sua análise de complexidade, é melhor mencionar: se n é o número de palavras em seu texto e m é o número de palavras diferentes (tipos, nós os chamamos), a etapa 1 é O ( n ), mas a etapa 2 é O ( m .lg ( m )) e m << n (você pode ter bilhões de palavras e não chegar a um milhão de tipos, experimente). Portanto, mesmo com um algoritmo fictício, ainda é O ( n + m lg ( m )) = O ( n ).
Nikana Reklawyks

1
Por favor, adicione uma suposição à questão de que temos memória principal suficiente para conter todas as palavras do texto grande. Seria interessante ver abordagens para encontrar k = 100 palavras em um arquivo de 10 GB (ou seja, todas as palavras não cabem em 4 GB de RAM) !!
KGhatak

@KGhatak como faríamos se excedesse o tamanho da RAM?
user7098526

Respostas:


66

Isso pode ser feito em tempo O (n)

Solução 1:

Passos:

  1. Conte as palavras e faça hash, o que vai acabar na estrutura como esta

    var hash = {
      "I" : 13,
      "like" : 3,
      "meow" : 3,
      "geek" : 3,
      "burger" : 2,
      "cat" : 1,
      "foo" : 100,
      ...
      ...
    
  2. Percorra o hash e encontre a palavra usada com mais frequência (neste caso, "foo" 100) e, em seguida, crie a matriz desse tamanho

  3. Então, podemos percorrer o hash novamente e usar o número de ocorrências de palavras como o índice do array, se não houver nada no índice, crie um array, senão anexe-o ao array. Então acabamos com uma matriz como:

      0   1      2            3                  100
    [[ ],[cat],[burger],[like, meow, geek],[]...[foo]]
    
  4. Em seguida, basta percorrer a matriz a partir do final e coletar as k palavras.

Solução 2:

Passos:

  1. O mesmo que acima
  2. Use min heap e mantenha o tamanho de min heap para k, e para cada palavra no hash comparamos as ocorrências de palavras com min, 1) se for maior que o valor min, remova o min (se o tamanho do min heap é igual a k) e insira o número no heap mínimo. 2) condições simples de descanso.
  3. Depois de percorrer o array, apenas convertemos o heap min em array e retornamos o array.

16
Sua solução (1) é uma classificação de balde O (n) substituindo uma classificação de comparação O (n lg n) padrão. Sua abordagem requer espaço adicional para a estrutura do balde, mas classificações de comparação podem ser feitas no local. Sua solução (2) é executada no tempo O (n lg k) - ou seja, O (n) para iterar todas as palavras e O (lg k) para adicionar cada uma à pilha.
stackoverflowuser2010

4
A primeira solução requer mais espaço, mas é importante enfatizar que é de fato O (n) no tempo. 1: Frequências de hash codificadas por palavra, O (n); 2: Hash de frequência transversal, crie um segundo hash codificado por frequência. Este é O (n) para percorrer o hash e O (1) para adicionar uma palavra à lista de palavras nessa frequência. 3: Percorra o hash para baixo da frequência máxima até atingir k. No máximo, O (n). Total = 3 * O (n) = O (n).
BringMyCakeBack

3
Normalmente, ao contar palavras, seu número de intervalos na solução 1 é amplamente superestimado (porque a palavra mais frequente número um é muito mais frequente do que a segunda e a terceira melhores), portanto, sua matriz é esparsa e ineficiente.
Nikana Reklawyks

Sua solução # 1 não funciona quando k (o número de palavras frequentes) é menor que o número de ocorrências da palavra mais frequente (ou seja, 100 neste caso). Claro, isso pode não acontecer na prática, mas deve-se não assumir!
One Two Three de

@OneTwoThree a solução proposta é apenas um exemplo. O número será baseado na demanda.
Chihung Yu de

22

Em geral, você não obterá um tempo de execução melhor do que a solução que descreveu. Você precisa fazer pelo menos O (n) trabalho para avaliar todas as palavras e, depois, O (k) trabalho extra para encontrar os k termos principais.

Se o seu conjunto de problemas for realmente grande, você pode usar uma solução distribuída como mapear / reduzir. Faça com que n trabalhadores do mapa contem frequências em 1 / enésimo do texto cada e, para cada palavra, envie para um dos trabalhadores do redutor m calculados com base no hash da palavra. Os redutores somam as contagens. A mesclagem de classificação sobre as saídas dos redutores fornecerá as palavras mais populares em ordem de popularidade.


13

Uma pequena variação em sua solução produz um algoritmo O (n) se não nos importarmos em classificar os K principais, e uma solução O (n + k * lg (k)) se o fizermos. Eu acredito que ambos os limites são ótimos dentro de um fator constante.

A otimização aqui vem novamente depois de percorrermos a lista, inserindo na tabela hash. Podemos usar o algoritmo da mediana das medianas para selecionar o K-ésimo maior elemento da lista. Este algoritmo é comprovadamente O (n).

Depois de selecionar o menor elemento K, particionamos a lista em torno desse elemento, assim como no quicksort. Obviamente, isso também é O (n). Qualquer coisa no lado "esquerdo" do pivô está em nosso grupo de elementos K, então terminamos (podemos simplesmente jogar fora todo o resto à medida que avançamos).

Portanto, esta estratégia é:

  1. Percorra cada palavra e insira-a em uma tabela hash: O (n)
  2. Selecione o menor elemento K: O (n)
  3. Partição em torno desse elemento: O (n)

Se você quiser classificar os K elementos, simplesmente classifique-os com qualquer classificação de comparação eficiente em tempo O (k * lg (k)), resultando em um tempo de execução total de O (n + k * lg (k)).

O limite de tempo O (n) é ótimo dentro de um fator constante porque devemos examinar cada palavra pelo menos uma vez.

O limite de tempo O (n + k * lg (k)) também é ótimo porque não há uma maneira baseada em comparação para classificar k elementos em menos de tempo k * lg (k).


Quando selecionamos o menor K-ésimo elemento, o que é selecionado é a menor K-ésima chave de hash. Não é necessário que haja exatamente K palavras na partição esquerda da Etapa 3.
Prakash Murali

2
Você não poderá executar "medianas de medianas" na tabela de hash, uma vez que faz as trocas. Você teria que copiar os dados da tabela hash para uma matriz temporária. Portanto, o armazenamento O (n) será necessário.
user674669

Não entendo como você pode selecionar o menor K-ésimo elemento em O (n)?
Michael Ho Chum

Verifique aqui o algoritmo para encontrar o menor elemento K em O (n) - wikiwand.com/en/Median_of_medians
Piyush

A complexidade é a mesma, mesmo se você usar uma tabela de hash + heap mínimo. não vejo nenhuma otimização.
Vinay de

8

Se a sua "lista grande de palavras" for grande o suficiente, você pode simplesmente amostrar e obter estimativas. Caso contrário, gosto de agregação de hash.

Editar :

Por amostra, quero dizer escolher algum subconjunto de páginas e calcular a palavra mais frequente nessas páginas. Desde que você selecione as páginas de forma razoável e selecione uma amostra estatisticamente significativa, suas estimativas das palavras mais frequentes devem ser razoáveis.

Essa abordagem só é realmente razoável se você tiver tantos dados que processá-los todos seja uma espécie de besteira. Se você tem apenas alguns megas, deve ser capaz de analisar os dados e calcular uma resposta exata sem suar muito, em vez de se preocupar em calcular uma estimativa.


Às vezes, você precisa fazer isso várias vezes, por exemplo, se estiver tentando obter a lista de palavras frequentes por site ou por assunto. Nesse caso, "sem suar a camisa" realmente não resolve. Você ainda precisa encontrar uma maneira de fazer isso da forma mais eficiente possível.
itsadok de

1
+1 para uma resposta prática que não aborde os problemas de complexidade irrelevantes. @itsadok: Para cada execução: se for grande o suficiente, faça uma amostra; se não for, ganhar um fator de log é irrelevante.
Nikana Reklawyks

2

Você pode reduzir ainda mais o tempo particionando usando a primeira letra das palavras e, em seguida, particionando o maior conjunto de várias palavras usando o próximo caractere até que você tenha k conjuntos de palavras únicas. Você usaria uma espécie de árvore de 256 maneiras com listas de palavras parciais / completas nas folhas. Você precisaria ter muito cuidado para não causar cópias de string em todos os lugares.

Este algoritmo é O (m), onde m é o número de caracteres. Isso evita a dependência de k, o que é muito bom para k grandes [pelo jeito que seu tempo de execução postado está errado, deveria ser O (n * lg (k)), e não tenho certeza do que isso significa em termos de m].

Se você executar os dois algoritmos lado a lado, obterá o que tenho certeza de que é um algoritmo O (min (m, n * lg (k))) assintoticamente ideal, mas o meu deve ser mais rápido em média porque não envolve hashing ou classificação.


7
O que você está descrevendo é chamado de 'trie'.
Nick Johnson,

Olá, Strilanc. Você pode explicar o processo de partição em detalhes?
Morgan Cheng,

1
como isso não envolve classificação? Depois de ter o teste, como você extrai as palavras k com as maiores frequências. não faz sentido
comum

2

Você tem um bug em sua descrição: a contagem leva O (n) tempo, mas a classificação leva O (m * lg (m)), onde m é o número de palavras únicas . Isso geralmente é muito menor do que o número total de palavras, então provavelmente deve apenas otimizar como o hash é construído.



2

Se o que você está procurando é a lista de k palavras mais frequentes em seu texto para qualquer k prático e para qualquer idioma natural, então a complexidade de seu algoritmo não é relevante.

Apenas amostra , digamos, alguns milhões de palavras de seu texto, processo que com qualquer algoritmo em questão de segundos , e conta mais freqüentes vai ser muito preciso.

Como uma observação lateral, a complexidade do algoritmo fictício (1. conte todos 2. classifique as contagens 3. pegue o melhor) é O (n + m * log (m)), onde m é o número de palavras diferentes em seu texto. log (m) é muito menor do que (n / m), então permanece O (n).

Praticamente, o passo longo é contar.


2
  1. Utilize uma estrutura de dados com eficiência de memória para armazenar as palavras
  2. Use MaxHeap para encontrar as K principais palavras frequentes.

Aqui está o código

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;

import com.nadeem.app.dsa.adt.Trie;
import com.nadeem.app.dsa.adt.Trie.TrieEntry;
import com.nadeem.app.dsa.adt.impl.TrieImpl;

public class TopKFrequentItems {

private int maxSize;

private Trie trie = new TrieImpl();
private PriorityQueue<TrieEntry> maxHeap;

public TopKFrequentItems(int k) {
    this.maxSize = k;
    this.maxHeap = new PriorityQueue<TrieEntry>(k, maxHeapComparator());
}

private Comparator<TrieEntry> maxHeapComparator() {
    return new Comparator<TrieEntry>() {
        @Override
        public int compare(TrieEntry o1, TrieEntry o2) {
            return o1.frequency - o2.frequency;
        }           
    };
}

public void add(String word) {
    this.trie.insert(word);
}

public List<TopK> getItems() {

    for (TrieEntry trieEntry : this.trie.getAll()) {
        if (this.maxHeap.size() < this.maxSize) {
            this.maxHeap.add(trieEntry);
        } else if (this.maxHeap.peek().frequency < trieEntry.frequency) {
            this.maxHeap.remove();
            this.maxHeap.add(trieEntry);
        }
    }
    List<TopK> result = new ArrayList<TopK>();
    for (TrieEntry entry : this.maxHeap) {
        result.add(new TopK(entry));
    }       
    return result;
}

public static class TopK {
    public String item;
    public int frequency;

    public TopK(String item, int frequency) {
        this.item = item;
        this.frequency = frequency;
    }
    public TopK(TrieEntry entry) {
        this(entry.word, entry.frequency);
    }
    @Override
    public String toString() {
        return String.format("TopK [item=%s, frequency=%s]", item, frequency);
    }
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + frequency;
        result = prime * result + ((item == null) ? 0 : item.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        TopK other = (TopK) obj;
        if (frequency != other.frequency)
            return false;
        if (item == null) {
            if (other.item != null)
                return false;
        } else if (!item.equals(other.item))
            return false;
        return true;
    }

}   

}

Aqui estão os testes de unidade

@Test
public void test() {
    TopKFrequentItems stream = new TopKFrequentItems(2);

    stream.add("hell");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("hero");
    stream.add("hero");
    stream.add("hero");
    stream.add("hello");
    stream.add("hello");
    stream.add("hello");
    stream.add("home");
    stream.add("go");
    stream.add("go");
    assertThat(stream.getItems()).hasSize(2).contains(new TopK("hero", 3), new TopK("hello", 8));
}

Para mais detalhes, consulte este caso de teste


1
  1. use uma tabela de hash para registrar a frequência de todas as palavras enquanto percorre toda a sequência de palavras. Nesta fase, a chave é "palavra" e o valor é "frequência de palavras". Isso leva tempo O (n). É o mesmo que cada um explicado acima

  2. Enquanto se insere no hashmap, mantenha o Treeet (específico para java, existem implementações em todas as linguagens) de tamanho 10 (k = 10) para manter as 10 palavras mais frequentes. Até que o tamanho seja menor que 10, continue adicionando. Se o tamanho for igual a 10, se o elemento inserido for maior que o elemento mínimo, ou seja, o primeiro elemento. Se sim, remova-o e insira um novo elemento

Para restringir o tamanho do conjunto de árvores, consulte este link


0

Suponha que tenhamos uma sequência de palavras "ad" "ad" "menino" "grande" "mau" "com" "venha" "frio". E K = 2. como você mencionou "particionamento usando a primeira letra das palavras", obtivemos ("ad", "ad") ("menino", "grande", "mau") ("com" "venha" "frio") "então particionar o maior conjunto de várias palavras usando o próximo caractere até que você tenha k conjuntos de palavras únicas. " ele irá particionar ("boy", "big", "bad") ("com" "come" "cold"), a primeira partição ("ad", "ad") é perdida, enquanto "ad" é ​​na verdade o palavra mais frequente.

Talvez eu não tenha entendido seu ponto. Você pode detalhar seu processo sobre partição?


0

Eu acredito que este problema pode ser resolvido por um algoritmo O (n). Poderíamos fazer a classificação na hora. Em outras palavras, a classificação nesse caso é um subproblema do problema de classificação tradicional, uma vez que apenas um contador é incrementado em um cada vez que acessamos a tabela hash. Inicialmente, a lista é classificada, uma vez que todos os contadores são zero. À medida que continuamos incrementando os contadores na tabela de hash, registramos outra matriz de valores de hash ordenados por frequência como segue. Cada vez que incrementamos um contador, verificamos seu índice no array classificado e verificamos se sua contagem excede seu predecessor na lista. Nesse caso, trocamos esses dois elementos. Como tal, obtemos uma solução que é no máximo O (n) onde n é o número de palavras no texto original.


Geralmente é uma boa direção - mas tem uma falha. quando a contagem for aumentada, não estaremos apenas verificando "seu predecessor", mas precisaremos verificar os "predecessores". por exemplo, há uma grande chance de que a matriz será [4,3,1,1,1,1,1,1,1,1,1,1] - os 1s podem ser tantos - o que o tornará menos eficiente já que teremos que olhar para trás em todos os predecessores para encontrar o apropriado para trocar.
Shawn

Isso não seria de fato muito pior do que O (n)? Mais como O (n ^ 2), pois é essencialmente uma classificação bastante ineficiente?
dcarr622

Oi Shawn. Sim, eu concordo com você. Mas suspeito que o problema que você mencionou seja fundamental para o problema. Na verdade, se em vez de manter apenas um array ordenado de valores, pudéssemos seguir em frente e manter um array de pares (valor, índice), onde o índice aponta para a primeira ocorrência do elemento repetido, o problema deve ser resolvido em O (n) tempo. Por exemplo, [4,3,1,1,1,1,1,1,1,1,1,1] será semelhante a [(4,0), (3,1), (1,2), (1 , 2), (1,2, ..., (1,2)]; os índices começam em 0.
Aly Farahat

0

Eu também estava lutando contra isso e me inspirei em @aly. Em vez de classificar depois, podemos apenas manter uma lista pré-classificada de palavras ( List<Set<String>>) e a palavra estará no conjunto na posição X, onde X é a contagem atual da palavra. Em geral, é assim que funciona:

  1. para cada palavra, armazená-lo como parte do mapa dele de ocorrência: Map<String, Integer>.
  2. em seguida, com base na contagem, remova-o do conjunto de contagem anterior e adicione-o ao novo conjunto de contagem.

A desvantagem disso é que a lista pode ser grande - pode ser otimizada usando um TreeMap<Integer, Set<String>>- mas isso adicionará alguma sobrecarga. Por fim, podemos usar uma combinação de HashMap ou nossa própria estrutura de dados.

O código

public class WordFrequencyCounter {
    private static final int WORD_SEPARATOR_MAX = 32; // UNICODE 0000-001F: control chars
    Map<String, MutableCounter> counters = new HashMap<String, MutableCounter>();
    List<Set<String>> reverseCounters = new ArrayList<Set<String>>();

    private static class MutableCounter {
        int i = 1;
    }

    public List<String> countMostFrequentWords(String text, int max) {
        int lastPosition = 0;
        int length = text.length();
        for (int i = 0; i < length; i++) {
            char c = text.charAt(i);
            if (c <= WORD_SEPARATOR_MAX) {
                if (i != lastPosition) {
                    String word = text.substring(lastPosition, i);
                    MutableCounter counter = counters.get(word);
                    if (counter == null) {
                        counter = new MutableCounter();
                        counters.put(word, counter);
                    } else {
                        Set<String> strings = reverseCounters.get(counter.i);
                        strings.remove(word);
                        counter.i ++;
                    }
                    addToReverseLookup(counter.i, word);
                }
                lastPosition = i + 1;
            }
        }

        List<String> ret = new ArrayList<String>();
        int count = 0;
        for (int i = reverseCounters.size() - 1; i >= 0; i--) {
            Set<String> strings = reverseCounters.get(i);
            for (String s : strings) {
                ret.add(s);
                System.out.print(s + ":" + i);
                count++;
                if (count == max) break;
            }
            if (count == max) break;
        }
        return ret;
    }

    private void addToReverseLookup(int count, String word) {
        while (count >= reverseCounters.size()) {
            reverseCounters.add(new HashSet<String>());
        }
        Set<String> strings = reverseCounters.get(count);
        strings.add(word);
    }

}

0

Acabei de descobrir a outra solução para este problema. Mas não tenho certeza se está certo. Solução:

  1. Use uma tabela de hash para registrar a frequência de todas as palavras T (n) = O (n)
  2. Escolha os primeiros k elementos da tabela hash e restaure-os em um buffer (cujo espaço = k). T (n) = O (k)
  3. Cada vez, primeiro precisamos encontrar o elemento min atual do buffer e apenas comparar o elemento min do buffer com os elementos (n - k) da tabela hash, um por um. Se o elemento da tabela hash for maior do que este elemento mínimo do buffer, elimine o mínimo do buffer atual e adicione o elemento da tabela hash. Assim, cada vez que encontrarmos o mínimo no buffer, precisamos de T (n) = O (k), e percorrermos toda a tabela hash, precisamos de T (n) = O (n - k). Portanto, toda a complexidade de tempo para esse processo é T (n) = O ((nk) * k).
  4. Depois de percorrer toda a tabela hash, o resultado está neste buffer.
  5. A complexidade de todo o tempo: T (n) = O (n) + O (k) + O (kn - k ^ 2) = O (kn + n - k ^ 2 + k). Uma vez que k é realmente menor do que n em geral. Portanto, para esta solução, a complexidade do tempo é T (n) = O (kn) . Esse é o tempo linear, quando k é realmente pequeno. Está certo? Eu realmente não tenho certeza.

0

Tente pensar em uma estrutura de dados especial para abordar esse tipo de problema. Neste caso, tipo especial de árvore, como a tentativa de armazenar strings de maneira específica, muito eficiente. Ou a segunda maneira de construir sua própria solução, como contar palavras. Eu acho que este TB de dados estaria em inglês, então temos cerca de 600.000 palavras em geral, então será possível armazenar apenas essas palavras e contar quais strings seriam repetidas + esta solução precisará de regex para eliminar alguns caracteres especiais. A primeira solução será mais rápida, tenho certeza.

http://en.wikipedia.org/wiki/Trie



0

Código mais simples para obter a ocorrência da palavra usada com mais frequência.

 function strOccurence(str){
    var arr = str.split(" ");
    var length = arr.length,temp = {},max; 
    while(length--){
    if(temp[arr[length]] == undefined && arr[length].trim().length > 0)
    {
        temp[arr[length]] = 1;
    }
    else if(arr[length].trim().length > 0)
    {
        temp[arr[length]] = temp[arr[length]] + 1;

    }
}
    console.log(temp);
    var max = [];
    for(i in temp)
    {
        max[temp[i]] = i;
    }
    console.log(max[max.length])
   //if you want second highest
   console.log(max[max.length - 2])
}

0

Nessas situações, recomendo o uso de recursos integrados do Java. Desde então, eles já estão bem testados e estáveis. Neste problema, encontro as repetições das palavras usando a estrutura de dados HashMap. Em seguida, envio os resultados para uma série de objetos. Classifico o objeto por Arrays.sort () e imprimo as k palavras principais e suas repetições.

import java.io.*;
import java.lang.reflect.Array;
import java.util.*;

public class TopKWordsTextFile {

    static class SortObject implements Comparable<SortObject>{

        private String key;
        private int value;

        public SortObject(String key, int value) {
            super();
            this.key = key;
            this.value = value;
        }

        @Override
        public int compareTo(SortObject o) {
            //descending order
            return o.value - this.value;
        }
    }


    public static void main(String[] args) {
        HashMap<String,Integer> hm = new HashMap<>();
        int k = 1;
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("words.in")));

            String line;
            while ((line = br.readLine()) != null) {
                // process the line.
                //System.out.println(line);
                String[] tokens = line.split(" ");
                for(int i=0; i<tokens.length; i++){
                    if(hm.containsKey(tokens[i])){
                        //If the key already exists
                        Integer prev = hm.get(tokens[i]);
                        hm.put(tokens[i],prev+1);
                    }else{
                        //If the key doesn't exist
                        hm.put(tokens[i],1);
                    }
                }
            }
            //Close the input
            br.close();
            //Print all words with their repetitions. You can use 3 for printing top 3 words.
            k = hm.size();
            // Get a set of the entries
            Set set = hm.entrySet();
            // Get an iterator
            Iterator i = set.iterator();
            int index = 0;
            // Display elements
            SortObject[] objects = new SortObject[hm.size()];
            while(i.hasNext()) {
                Map.Entry e = (Map.Entry)i.next();
                //System.out.print("Key: "+e.getKey() + ": ");
                //System.out.println(" Value: "+e.getValue());
                String tempS = (String) e.getKey();
                int tempI = (int) e.getValue();
                objects[index] = new SortObject(tempS,tempI);
                index++;
            }
            System.out.println();
            //Sort the array
            Arrays.sort(objects);
            //Print top k
            for(int j=0; j<k; j++){
                System.out.println(objects[j].key+":"+objects[j].value);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

Para obter mais informações, visite https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKWordsTextFile.java . Espero que ajude.


De que forma isso melhora a abordagem esboçada na pergunta? (Por favor, não deixe comentários do código apresentado no SE.) ( I recommend to use Java built-in featuresComo loops foreach e processamento de streams ?)
barba cinza

Como você sabe, um dos fatores mais importantes no projeto de um algoritmo eficiente é escolher a estrutura de dados certa. Então, é importante como você aborda o problema. Por exemplo, você precisa atacar um problema dividindo para conquistar. Você precisa atacar outro ganancioso. Como você sabe, a empresa Oracle está trabalhando em Java. Eles são uma das melhores empresas de tecnologia do mundo. Existem alguns dos engenheiros mais brilhantes trabalhando lá nos recursos integrados do Java. Portanto, esses recursos são bem testados e à prova de balas. Se podemos utilizá-los, é melhor usá-los em minha opinião.
Mohammad

0
**

C ++ 11 Implementação do pensamento acima

**

class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {

    unordered_map<int,int> map;
    for(int num : nums){
        map[num]++;
    }

    vector<int> res;
    // we use the priority queue, like the max-heap , we will keep (size-k) smallest elements in the queue
    // pair<first, second>: first is frequency,  second is number 
    priority_queue<pair<int,int>> pq; 
    for(auto it = map.begin(); it != map.end(); it++){
        pq.push(make_pair(it->second, it->first));

        // onece the size bigger than size-k, we will pop the value, which is the top k frequent element value 

        if(pq.size() > (int)map.size() - k){
            res.push_back(pq.top().second);
            pq.pop();
        }
    }
    return res;

}

};

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.