Самый эффективный способ найти K самых частых слов в последовательности больших слов

86

Вход: положительное целое число K и большой текст. Фактически текст можно рассматривать как последовательность слов. Поэтому нам не нужно беспокоиться о том, как разбить его на последовательность слов.
Вывод: K наиболее часто встречающихся слов в тексте.

Я думаю так.

  1. используйте хеш-таблицу для записи частоты всех слов при обходе всей последовательности слов. На этом этапе ключом является «слово», а значением - «частота слова». Это занимает O (n) времени.

  2. отсортировать пару (слово, частота слова); и ключ - "частота слов". Это занимает O (n * lg (n)) времени с обычным алгоритмом сортировки.

  3. После сортировки мы берем только первые K слов. Это занимает O (K) времени.

Подводя итог, общее время составляет O (n + n lg (n) + K) , Поскольку K, безусловно, меньше N, то на самом деле это O (n lg (n)).

Мы можем это улучшить. На самом деле, нам просто нужны первые K слов. Частота других слов нас не волнует. Итак, мы можем использовать «частичную сортировку кучи». На шагах 2) и 3) мы не просто выполняем сортировку. Вместо этого мы меняем его на

2 ') построить кучу пар (слово, частота слова) с ключом "частота слова". На создание кучи уходит O (n) времени;

3 ') извлечь из кучи верхних K слов. Каждое извлечение составляет O (lg (n)). Итак, общее время O (k * lg (n)).

Подводя итог, это решение стоит времени O (n + k * lg (n)).

Это просто моя мысль. Я не нашел способа улучшить шаг 1).
Я надеюсь, что некоторые специалисты по информационному поиску смогут пролить больше света на этот вопрос.

Морган Ченг
источник
Вы бы использовали сортировку слиянием или быструю сортировку для сортировки O (n * logn)?
совершиландроидер
1
Для практического использования лучше всего подходит ответ Аарона Маенпаа - расчет по образцу. Не похоже, что самые частые слова скроются из вашего образца. Для любителей сложности это O (1), поскольку размер выборки фиксирован. Вы не получаете точных подсчетов, но и не спрашиваете их.
Никана Реклавикс
Если вам нужен обзор вашего анализа сложности, то я лучше упомяну: если n - количество слов в вашем тексте, а m - количество разных слов (мы называем их типами), шаг 1 - это O ( n ), но шаг 2 - O ( m. lg ( m )) и m << n (у вас могут быть миллиарды слов и не достигнуть миллиона типов, попробуйте). Таким образом, даже с фиктивным алгоритмом это все равно O ( n + m lg ( m )) = O ( n ).
Никана Реклавикс
1
Пожалуйста, добавьте к вопросу предположение, что у нас достаточно основной памяти для хранения всех слов большого текста. Было бы интересно увидеть подходы к поиску k = 100 слов из файла размером 10 ГБ (т.е. все слова не поместятся в 4 ГБ ОЗУ) !!
KGhatak
@KGhatak, как бы мы это сделали, если он превышает размер ОЗУ?
user7098526

Ответы:

67

Это можно сделать за O (n) раз

Решение 1:

Шаги:

  1. Подсчитайте слова и хешируйте их, в итоге получится такая структура.

    var hash = {
      "I" : 13,
      "like" : 3,
      "meow" : 3,
      "geek" : 3,
      "burger" : 2,
      "cat" : 1,
      "foo" : 100,
      ...
      ...
    
  2. Просмотрите хеш и найдите наиболее часто используемое слово (в данном случае "foo" 100), затем создайте массив такого размера.

  3. Затем мы можем снова пройти по хешу и использовать количество вхождений слов в качестве индекса массива, если в индексе ничего нет, создайте массив, иначе добавьте его в массив. В итоге мы получаем такой массив:

      0   1      2            3                  100
    [[ ],[cat],[burger],[like, meow, geek],[]...[foo]]
    
  4. Затем просто пройдитесь по массиву с конца и соберите k слов.

Решение 2:

Шаги:

  1. То же, что и выше
  2. Используйте min heap и сохраните размер min heap равным k, и для каждого слова в хэше мы сравниваем вхождения слов с min, 1), если оно больше минимального значения, удалите min (если размер min heap равно k) и вставьте число в min heap. 2) остальные простые условия.
  3. После обхода массива мы просто конвертируем минимальную кучу в массив и возвращаем массив.
Чихунг Ю
источник
16
Ваше решение (1) - это сортировка ведра O (n), заменяющая стандартную сортировку сравнения O (n lg n). Ваш подход требует дополнительного места для структуры корзины, но сортировки сравнения можно выполнять на месте. Ваше решение (2) выполняется за время O (n lg k), то есть O (n) для перебора всех слов и O (lg k) для добавления каждого из них в кучу.
stackoverflowuser2010
4
Первое решение требует больше места, но важно подчеркнуть, что на самом деле это O (n) во времени. 1: частота хеширования, определяемая словом, O (n); 2: Пересечение хеша частоты, создание второго хеша с ключом частоты. Это O (n) для обхода хэша и O (1) для добавления слова в список слов с этой частотой. 3: Пройдите по хешу вниз от максимальной частоты, пока не достигнете k. Максимум O (n). Итого = 3 * O (n) = O (n).
BringMyCakeBack
3
Обычно при подсчете слов ваше количество сегментов в решении 1 сильно переоценивается (потому что самое частое слово номер один намного чаще, чем второе и третье лучшие), поэтому ваш массив разрежен и неэффективен.
Никана Реклавикс
Ваше решение №1 не работает, когда k (количество часто встречающихся слов) меньше, чем количество вхождений наиболее частого слова (например, в данном случае 100). Конечно, на практике этого может не случиться, но следует не предполагаю!
One Two Three,
@OneTwoThree предлагаемое решение является лишь примером. Количество будет зависеть от спроса.
Чихунг Ю
22

Как правило, вы не получите лучшего времени выполнения, чем описанное вами решение. Вы должны выполнить как минимум O (n) работы, чтобы оценить все слова, а затем O (k) дополнительной работы, чтобы найти верхние k терминов.

Если ваш набор задач действительно велик, вы можете использовать распределенное решение, такое как map / reduce. Пусть n обработчиков карт подсчитывают частоту для каждой 1 / n-ой текста, и для каждого слова отправляйте его одному из m рабочих редукторов, рассчитанных на основе хэша слова. Затем редукторы суммируют счет. Сортировка слиянием результатов редукторов даст вам самые популярные слова в порядке их популярности.

Ник Джонсон
источник
13

Небольшая вариация вашего решения дает алгоритм O (n) , если мы не заботимся о ранжировании верхнего K, и решение O (n + k * lg (k)), если мы это делаем. Я считаю, что обе эти границы оптимальны в пределах постоянного множителя.

Оптимизация здесь снова происходит после того, как мы пробежимся по списку, вставив его в хеш-таблицу. Мы можем использовать алгоритм медианы медиан , чтобы выбрать K-й по величине элемент в списке. Этот алгоритм доказуемо O (n).

После выбора K-го наименьшего элемента мы разбиваем список вокруг этого элемента так же, как при быстрой сортировке. Очевидно, это тоже O (n). Все, что находится на «левой» стороне оси вращения, находится в нашей группе из K элементов, так что мы закончили (мы можем просто выбросить все остальное по мере продвижения).

Итак, эта стратегия такова:

  1. Просмотрите каждое слово и вставьте его в хеш-таблицу: O (n)
  2. Выберите K-й наименьший элемент: O (n)
  3. Разделение вокруг этого элемента: O (n)

Если вы хотите ранжировать K элементов, просто отсортируйте их с помощью любой эффективной сортировки сравнения за время O (k * lg (k)), что даст общее время выполнения O (n + k * lg (k)).

Граница времени O (n) оптимальна в пределах постоянного множителя, потому что мы должны проверять каждое слово хотя бы один раз.

Граница времени O (n + k * lg (k)) также оптимальна, потому что не существует основанного на сравнении способа отсортировать k элементов за время меньше, чем k * lg (k).


источник
Когда мы выбираем K-й наименьший элемент, выбирается K-й наименьший хэш-ключ. Необязательно, чтобы в левом разделе Шага 3 было ровно K слов.
Пракаш Мурали
2
Вы не сможете запускать «медианы медиан» в хеш-таблице, поскольку она меняет местами. Вам нужно будет скопировать данные из хеш-таблицы во временный массив. Итак, потребуется O (n) памяти.
user674669
Я не понимаю, как можно выбрать наименьший K-й элемент в O (n)?
Майкл Хо Чам,
Проверьте это для алгоритма для нахождения k - ю наименьший элемент в O (N) - wikiwand.com/en/Median_of_medians
Piyush
Сложность такая же, даже если вы используете хеш-таблицу + минимальную кучу. я не вижу никакой оптимизации.
Vinay
8

Если ваш «большой список слов» достаточно велик, вы можете просто выбрать и получить оценки. В остальном мне нравится агрегация хешей.

Редактировать :

Под образцом я подразумеваю выбор некоторого подмножества страниц и вычисление наиболее частого слова на этих страницах. Если вы выбираете страницы разумным образом и выбираете статистически значимую выборку, ваши оценки наиболее часто используемых слов должны быть разумными.

Этот подход действительно разумен только в том случае, если у вас так много данных, что обрабатывать их все просто глупо. Если у вас всего несколько мегабайт, вы сможете просмотреть данные и вычислить точный ответ, не беспокоясь, вместо того, чтобы беспокоиться о подсчете оценки.

Аарон Маенпаа
источник
Иногда вам приходится делать это много раз, например, если вы пытаетесь получить список часто используемых слов для каждого веб-сайта или темы. В этом случае «не вспотеть» на самом деле не поможет. Вам все еще нужно найти способ сделать это максимально эффективно.
itsadok
1
+1 за практический ответ, не затрагивающий несущественные вопросы сложности. @itsadok: Для каждого прогона: если он достаточно большой, выберите его; если это не так, то получение лог-фактора не имеет значения.
Никана Реклавикс
2

Вы можете еще больше сократить время, разбив на части по первой букве слов, а затем разбив самый большой набор из нескольких слов с помощью следующего символа, пока не получите k наборов из одного слова. Вы могли бы использовать 256-стороннее дерево сортировки со списками частичных / полных слов на концах. Вам нужно будет быть очень осторожным, чтобы не создавать копии строк повсюду.

Это алгоритм O (m), где m - количество символов. Это позволяет избежать этой зависимости от k, что очень хорошо для больших k [кстати, ваше опубликованное время работы неверно, оно должно быть O (n * lg (k)), и я не уверен, что это с точки зрения м].

Если вы запустите оба алгоритма бок о бок, вы получите то, что, я уверен, является асимптотически оптимальным алгоритмом O (min (m, n * lg (k))), но мой в среднем должен быть быстрее, потому что он не включает хеширование или сортировка.


источник
7
То, что вы описываете, называется «trie».
Ник Джонсон,
Привет, Стриланц. Вы можете подробно объяснить процесс разбиения?
Морган Ченг,
1
как это не связано с сортировкой ?? как только у вас есть trie, как вы можете выделить k слов с наибольшей частотой. не имеет никакого смысла
обычный
2

В вашем описании есть ошибка: подсчет занимает O (n) времени, а сортировка занимает O (m * lg (m)), где m - количество уникальных слов. Обычно это намного меньше, чем общее количество слов, поэтому, вероятно, следует просто оптимизировать способ построения хеша.

Мартин
источник
2

Если вам нужен список из k наиболее часто встречающихся слов в вашем тексте для любого практического k и для любого естественного языка, то сложность вашего алгоритма не имеет значения.

Просто пример , скажем, несколько миллионов слов из текста, процесс , который с любым алгоритмом в считанные секунды , и наиболее частых импульсов будет очень точным.

В качестве примечания: сложность фиктивного алгоритма (1. подсчитать все 2. отсортировать подсчеты 3. выбрать лучшее) составляет O (n + m * log (m)), где m - количество разных слов в вашем текст. log (m) намного меньше, чем (n / m), поэтому остается O (n).

Практически длинный шаг на счету.

Никана Реклавикс
источник
2
  1. Используйте эффективную структуру данных для хранения слов
  2. Используйте MaxHeap, чтобы найти K самых частых слов.

Вот код

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;
    }

}   

}

Вот модульные тесты

@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));
}

Для получения дополнительной информации см. Этот тестовый пример

ремесленник
источник
1
  1. используйте хеш-таблицу для записи частоты всех слов при обходе всей последовательности слов. На этом этапе ключом является «слово», а значением - «частота слова». Это занимает O (n) времени. Это то же самое, что и все, что описано выше.

  2. Во время самой вставки в хэш-карту сохраните Treeset (специфичный для java, есть реализации на каждом языке) размером 10 (k = 10), чтобы сохранить 10 самых часто используемых слов. Пока размер меньше 10, продолжайте добавлять. Если размер равен 10, если вставленный элемент больше минимального элемента, то есть первого элемента. Если да, удалите его и вставьте новый элемент

Чтобы ограничить размер набора деревьев, перейдите по этой ссылке

M Sach
источник
0

Предположим, у нас есть последовательность слов «ad» «ad» «boy» «big» «bad» «com» ​​«come» «cold». И К = 2. как вы упомянули "разбиение по первой букве слов", мы получили ("ad", "ad") ("boy", "big", "bad") ("com" "come" "cold") "then разбивая самый большой набор из нескольких слов с помощью следующего символа, пока не будет k наборов из одного слова. " он будет разделен («мальчик», «большой», «плохой») («com» ​​«come» «холодный»), первый раздел («ad», «ad») будет пропущен, а «ad» - это фактически самое частое слово.

Возможно, я неправильно понимаю вашу точку зрения. Не могли бы вы подробно описать процесс создания разделов?

Морган Ченг
источник
0

Я считаю, что эту проблему можно решить с помощью алгоритма O (n). Мы могли производить сортировку на лету. Другими словами, сортировка в этом случае является подзадачей традиционной задачи сортировки, поскольку только один счетчик увеличивается на единицу каждый раз, когда мы обращаемся к хеш-таблице. Изначально список отсортирован, так как все счетчики равны нулю. Поскольку мы продолжаем увеличивать счетчики в хеш-таблице, мы сохраняем еще один массив хеш-значений, упорядоченных по частоте следующим образом. Каждый раз, когда мы увеличиваем счетчик, мы проверяем его индекс в ранжированном массиве и проверяем, превышает ли его счетчик значение своего предшественника в списке. Если это так, мы меняем эти два элемента местами. Таким образом, мы получаем решение, которое не превышает O (n), где n - количество слов в исходном тексте.

Али Фарахат
источник
В целом это хорошее направление, но у него есть недостаток. когда счетчик увеличивается, мы не будем просто проверять «его предшественника», но нам нужно будет проверить «предшественников». например, есть большая вероятность, что массив будет [4,3,1,1,1,1,1,1,1,1,1] - единиц может быть столько же, что сделает его менее эффективным. так как нам придется просмотреть всех предшественников, чтобы найти подходящий для замены.
Шон
Разве это не было бы хуже, чем O (n)? Больше похоже на O (n ^ 2), поскольку это довольно неэффективная сортировка?
dcarr622
Привет, Шон. Да, я согласен с вами. Но я подозреваю, что упомянутая вами проблема является фундаментальной для проблемы. Фактически, если вместо хранения только отсортированного массива значений мы могли бы продолжить сохранение массива пар (значение, индекс), где индекс указывает на первое вхождение повторяющегося элемента, проблема должна быть решена за O (п) время. Например, [4,3,1,1,1,1,1,1,1,1,1] будет выглядеть как [(4,0), (3,1), (1,2), (1 , 2), (1,2, ..., (1,2)]; индексы начинаются с 0.
Али Фарахат
0

Я тоже боролся с этим и меня вдохновил @aly. Вместо последующей сортировки мы можем просто поддерживать предварительно отсортированный список слов ( List<Set<String>>), и слово будет в наборе в позиции X, где X - текущий счетчик слова. В общем, вот как это работает:

  1. для каждого слова сохраните его как часть карты его появления: Map<String, Integer> .
  2. затем, основываясь на подсчете, удалите его из предыдущего набора счетчиков и добавьте в новый набор счетчиков.

Недостатком этого является то, что список может быть большим - его можно оптимизировать, используя TreeMap<Integer, Set<String>> - но это добавит накладных расходов. В конечном итоге мы можем использовать смесь HashMap или нашу собственную структуру данных.

Код

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

Я просто нашел другое решение этой проблемы. Но я не уверен, что это правильно. Решение:

  1. Используйте хеш-таблицу для записи частоты всех слов T (n) = O (n)
  2. Выберите первые k элементов хеш-таблицы и восстановите их в одном буфере (в котором пространство = k). Т (п) = О (к)
  3. Каждый раз, в первую очередь, нам нужно найти текущий минимальный элемент буфера и просто сравнить минимальный элемент буфера с (n - k) элементами хеш-таблицы один за другим. Если элемент хеш-таблицы больше, чем этот минимальный элемент буфера, тогда отбрасывается min текущего буфера и добавляется элемент хеш-таблицы. Таким образом, каждый раз, когда мы находим минимальную в буфере, нужно T (n) = O (k), а для обхода всей хеш-таблицы нужно T (n) = O (n - k). Таким образом, вся временная сложность этого процесса равна T (n) = O ((nk) * k).
  4. После обхода всей хеш-таблицы результат находится в этом буфере.
  5. Полная временная сложность: T (n) = O (n) + O (k) + O (kn - k ^ 2) = O (kn + n - k ^ 2 + k). Так как k действительно меньше n в целом. Таким образом, для этого решения временная сложность T (n) = O (kn) . Это линейное время, когда k действительно мало. Это правильно? Я действительно не уверен.
zproject89
источник
0

Постарайтесь придумать особую структуру данных для решения таких проблем. В этом случае особый вид дерева, такой как trie, для хранения строк определенным образом, очень эффективен. Или второй способ создать собственное решение, например, подсчет слов. Я предполагаю, что этот ТБ данных будет на английском языке, тогда у нас в целом около 600000 слов, поэтому можно будет хранить только эти слова и подсчитать, какие строки будут повторяться + этому решению потребуется регулярное выражение для устранения некоторых специальных символов. Я уверен, что первое решение будет быстрее.

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

черника0xff
источник
0

Самый простой код для определения появления наиболее часто используемого слова.

 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])
}
ngLover
источник
0

В таких ситуациях я рекомендую использовать встроенные функции Java. Поскольку они уже хорошо протестированы и стабильны. В этой задаче я нахожу повторения слов с помощью структуры данных HashMap. Затем я помещаю результаты в массив объектов. Я сортирую объект по Arrays.sort () и печатаю k первых слов и их повторы.

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();
        }
    }

}

Для получения дополнительной информации посетите https://github.com/m-vahidalizadeh/foundations/blob/master/src/algorithms/TopKWordsTextFile.java . Я надеюсь, что это помогает.

Мохаммад
источник
Каким образом это улучшает подход, описанный в вопросе? (Пожалуйста, не оставляйте комментарии из кода, представленного на SE.) ( I recommend to use Java built-in featuresНапример, циклы foreach и обработка потоков ?)
greybeard
Как вы знаете, одним из наиболее важных факторов при разработке эффективного алгоритма является выбор правильной структуры данных. Затем важно, как вы подойдете к проблеме. Например, вам нужно решить проблему, разделяя и властвуй. Вам нужно напасть на другого жадно. Как вы знаете, компания Oracle работает над Java. Это одна из лучших технологических компаний в мире. Над встроенными функциями Java работают одни из самых блестящих инженеров. Итак, эти функции хорошо протестированы и надежны. Если мы можем их использовать, на мой взгляд, лучше использовать их.
Мохаммад
0
**

C ++ 11 Реализация вышеизложенной мысли

**

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;

}

};

asad_nitp
источник