Найефективніший спосіб знайти найпопулярніші слова K у великій послідовності слів


85

Вхідні дані: Позитивне ціле число 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), оскільки розмір вибірки фіксований. Ви не отримуєте точних підрахунків, але ви також не запитуєте їх.
Nikana Reklawyks

Якщо те, що ви хочете, - це огляд вашого аналізу складності, то я краще згадаю: якщо n - кількість слів у вашому тексті, а m - кількість різних слів (типів, ми їх називаємо), крок 1 - O ( n ), але кроком 2 є O ( m .lg ( m )), і m << n (у вас може бути мільярди слів, і ви не досягли мільйона типів, спробуйте). Отже, навіть з фіктивним алгоритмом, це все одно O ( n + m lg ( m )) = O ( n ).
Nikana Reklawyks

1
Будь ласка, додайте припущення до питання, що у нас достатньо основної пам’яті, щоб вмістити всі слова великого тексту. Було б цікаво побачити підходи для пошуку k = 100 слів з файлу 10 ГБ (тобто всі слова не поміщаються в 4 ГБ оперативної пам'яті) !!
KGhatak

@KGhatak як би ми це зробили, якщо він перевищує розмір оперативної пам'яті?
user7098526

Відповіді:


66

Це можна зробити за 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 (якщо розмір min heap дорівнює k) і вставте число в min кучу. 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 набагато частіше, ніж друге та третє найкраще), тому ваш масив є рідкісним та неефективним.
Nikana Reklawyks

Ваше рішення №1 не працює, коли k (кількість частих слів) менша за кількість зустрічаються найчастіших слів (тобто 100 у цьому випадку) Звичайно, це може не відбутися на практиці, але слід не припускати!
One Two Three

@OneTwoThree запропоноване рішення є лише прикладом. Кількість буде залежати від попиту.
Chihung Yu

22

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

Якщо ваш набір дійсно великий, ви можете використовувати розподілене рішення, таке як map / зменшення. Попросіть 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)?
Michael Ho Chum

Перевірте це для алгоритму для знаходження k - ю найменший елемент в O (N) - wikiwand.com/en/Median_of_medians
Piyush

Складність однакова, навіть якщо ви використовуєте хеш-таблицю + мінімальну купу. я не бачу ніякої оптимізації.
Vinay

8

Якщо ваш "великий список слів" достатньо великий, ви можете просто взяти вибірку та отримати оцінки. В іншому випадку мені подобається агрегування хешу.

Редагувати :

Під зразком я маю на увазі вибрати деяку підмножину сторінок і обчислити найпоширеніше слово на цих сторінках. За умови, що ви обираєте сторінки розумно і вибираєте статистично значущу вибірку, ваші оцінки найпоширеніших слів повинні бути обґрунтованими.

Такий підхід насправді розумний лише в тому випадку, якщо у вас так багато даних, що обробляти їх все просто дурно. Якщо у вас лише кілька мегабайт, ви зможете переглядати дані та розраховувати точну відповідь, не потіючи потом, а не турбуючись про підрахунок оцінки.


Іноді це доводиться робити багато разів, наприклад, якщо ви намагаєтеся отримати список частих слів для веб-сайту або для теми. У такому випадку "не потіючи піт" насправді це не ріже. Вам все одно потрібно знайти спосіб зробити це якомога ефективніше.
іцадок

1
+1 за практичну відповідь, яка не стосується невідповідних питань складності. @itsadok: Для кожного запуску: якщо він достатньо великий, відвідайте його; якщо це не так, то отримання коефіцієнта журналу не має значення.
Nikana Reklawyks

2

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

Цей алгоритм дорівнює O (m), де m - кількість символів. Це дозволяє уникнути такої залежності від k, що дуже приємно для великих k [до речі, ваш опублікований час роботи неправильний, він повинен бути O (n * lg (k)), і я не впевнений, що це з точки зору м].

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


7
Те, що ви описуєте, називається "триє".
Нік Джонсон,

Привіт Стріланко. Чи можете ви детально пояснити процес розділення?
Морган Ченг,

1
як це не передбачає сортування ?? як тільки ви отримаєте трие, як ви вирвати 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, якщо вставлений елемент більше мінімального елемента, тобто першого елемента. Якщо так, видаліть його та вставте новий елемент

Щоб обмежити розмір набору дерев, дивіться це посилання


0

Припустимо, у нас є послідовність слів "ad" "ad" "boy" "big" "bad" "com" "come" "cold". І K = 2. як ви вже згадали "розділення з використанням першої літери слів", ми отримали ("ad", "ad") ("boy", "big", "bad") ("com" "come" "cold") "then розділити найбільший набір із багатьох слів, використовуючи наступний символ, поки у вас не буде k наборів з одного слова. він буде розділом ("хлопчик", "великий", "поганий") ("ком" "прийде" "холодним"), перший розділ ("реклама", "реклама") пропущений, тоді як "реклама" насправді є найчастіше слово.

Можливо, я неправильно розумію вашу думку. Чи можете ви детально описати ваш процес щодо розділу?


0

Я вважаю, що цю проблему можна вирішити за допомогою алгоритму O (n). Ми могли б здійснити сортування на льоту. Іншими словами, сортування в цьому випадку є підзадачею традиційної проблеми сортування, оскільки лише один лічильник збільшується на одиницю кожного разу, коли ми отримуємо доступ до хеш-таблиці. Спочатку список сортується, оскільки всі лічильники дорівнюють нулю. Оскільки ми продовжуємо нарощувати лічильники в хеш-таблиці, ми зберігаємо ще один масив хеш-значень, упорядкованих за частотою, як показано нижче. Кожного разу, коли ми збільшуємо лічильник, ми перевіряємо його індекс у рангованому масиві та перевіряємо, чи перевищує його кількість попередника у списку. Якщо так, ми міняємо місцями ці два елементи. Таким чином ми отримуємо рішення, яке становить не більше O (n), де n - кількість слів у вихідному тексті.


Як правило, це хороший напрямок, але він має недолік. коли кількість буде збільшена, ми не будемо просто перевіряти "попередника", а нам потрібно буде перевірити "попередників". наприклад, є великий шанс, що масив буде [4,3,1,1,1,1,1,1,1,1,1] - одиниць може бути стільки - що зробить його менш ефективним оскільки нам доведеться переглянути всі попередники, щоб знайти відповідного для обміну.
Шон

Чи насправді це не було б набагато гірше, ніж O (n)? Більше схожий на O (n ^ 2), оскільки це, по суті, досить неефективний сорт?
dcarr622

Привіт Шон. Так, я з вами згоден. Але я підозрюю, що згадана вами проблема є фундаментальною для проблеми. Насправді, якщо замість того, щоб зберегти лише відсортований масив значень, ми могли б продовжити масив з пар (значення, індекс), де індекс вказує на перше входження повторюваного елемента, проблема повинна бути розв'язуваною в O (n) час. Наприклад, [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). T (n) = O (k)
  3. Кожного разу, по-перше, нам потрібно знайти поточний елемент min буфера і просто порівняти min елемент буфера з (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 дійсно малий. Це право? Я справді не впевнений.

0

Спробуйте продумати спеціальну структуру даних, щоб підійти до такого роду проблем. У цьому випадку особливий вид дерева, як трие, для зберігання рядків певним чином, дуже ефективний. Або другий спосіб побудувати власне рішення, як підрахунок слів. Я думаю, цей ТБ даних буде англійською мовою, тоді загалом у нас близько 600 000 слів, тому можна буде зберігати лише ті слова та підраховувати, які рядки будуть повторюватися + це рішення потребуватиме регулярного виразу для усунення деяких спеціальних символів. Перше рішення буде швидшим, я впевнений.

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



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])
}

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Як цикл перегляду і потоків обробки ?)
глиняний глечик

Як відомо, одним із найважливіших факторів при розробці ефективного алгоритму є вибір правильної структури даних. Тоді важливо, як ви підходите до проблеми. Наприклад, вам потрібно атакувати проблему, розділяючи і завойовуючи. Вам потрібно напасти на ще одного жадібним. Як відомо, компанія 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;

}

};

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.