Частота слова с упорядочением по сложности O (n)

11

Во время собеседования на должность разработчика Java меня спросили следующее:

Напишите функцию, которая принимает два параметра:

  1. Строка, представляющая текстовый документ и
  2. целое число, представляющее количество возвращаемых предметов.

Реализуйте функцию так, чтобы она возвращала список Строк, упорядоченных по частоте слова, первое из которых встречается чаще всего. Ваше решение должно выполняться за время О(N) где N - количество символов в документе.

Вот что я ответил (в псевдокоде), это не О(N) а скорее О(NжурналN) время из-за сортировки. Я не могу понять, как это сделать О(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

Кто-то знает, или кто-то может дать мне несколько советов?

user2712937
источник
1
Используйте хеш-таблицу.
Юваль Фильмус
Использование хеш-таблицы не решает проблему. Кроме того, hashtable является устаревшей Java.
user2712937
Хеш-таблицы обычно являются хитростью, позволяющей снизить сложность от до O ( n ) . Даже если это устаревшая Java, что бы это ни значило. Я не проверял этот конкретный случай, так что вы можете быть правы. O(nlogn)O(n)
Юваль Фильмус
@YuvalFilmus. Спасибо, но хеш-таблица во многом похожа на хеш-карту, которую я уже использую (основное отличие между 2 структурами данных - это синхронизация, которая здесь не применяется). Журнал (n) в моем случае происходит из сортировки значений в хэш-карте.
user2712937
3
Кстати, этот сайт ориентирован на концепции и алгоритмы, а не на код. Поэтому обычно мы просим вас удалить код Java и дать концептуальное описание вашего подхода (возможно, с кратким псевдокодом высокого уровня, если это необходимо). Также на этом сайте актуален вопрос, какие структуры данных и алгоритмы использовать; конкретный API Java не по теме для этого сайта (но вы могли бы спросить об этом в StackOverflow), и, аналогично, Hashtableявляется ли устаревшая Java или нет действительно неуместной для целей этого сайта.
DW

Ответы:

10

Я предлагаю вариант подсчета распределения:

  1. Прочитайте текст и вставьте все слова, встречающиеся в три , сохраняя в каждом узле счетчик того, как часто встречается слово, представленное этим узлом. Дополнительно следите за наибольшим количеством слов, скажем maxWordCound. - О(N)
  2. Инициализируйте массив размера maxWordCount. Тип записи - это списки строк. - , так как количество не может быть выше.О(N)
  3. Пройдите через три и для каждого узла добавьте соответствующую строку к записи массива, указанной счетчиком. - , так как общая длина строк ограничена n .О(N)N
  4. Пройдите по массиву в порядке убывания и выведите желаемое количество строк. - , поскольку это ограничение как размера, так и количества данных в массиве.О(N)

Возможно, вы можете заменить три других структур данных на первом этапе.

FrankW
источник
+1, хотя я не уверен в этом. Это O (n), поскольку количество возвращаемых слов ограничено n, количеством символов, но задает ли это вопрос? Или результат не зависит от количества возвращаемых слов?
Никос М.
@NikosM. Он является ; - это общая верхняя граница для числа возвращаемых слов в худшем случае, а не необходимые предположения. N
Рафаэль
@ Рафаэль, да, правильно, я думаю об этом, так как это было задано в интервью, возможные уловки в вопросе.
Никос М.
Мне интересно, существует ли эффективный в пространстве алгоритм линейного времени.
saadtaame
3
@saadtaame, да, это интересный вопрос. Возможно, стоит разместить отдельно как отдельный вопрос. Это не просто космическая эффективность; Трехуровневое решение также интенсивно использует указатели, что на практике может замедлить его работу (учитывая, как иерархия памяти работает на реальных машинах). «Эффективность» отличается от наихудшего времени работы. Нет ничего необычного в том, что чистый алгоритм времени побеждает алгоритм времени O ( n ) с интенсивным указателем , поэтому этот вопрос уже, кажется, исключает некоторые потенциальные алгоритмы, которые могут быть лучшим выбором на практике. О(NЛ.Г.N)О(N)
DW
3

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

Куча - это распространенный способ агрегирования верхних значений k, хотя могут использоваться и другие методы (см. Https://en.wikipedia.org/wiki/Partial_sorting ).

Предполагая, что k является вторым параметром выше, и что это константа в постановке задачи (она выглядит так):

  1. Создайте три слова с количеством вхождений на каждом узле.
  2. Инициализируйте кучу размера k.
  3. Пройдите три и мин-зонд / вставьте каждую пару (лист, число-количество) в кучу top-k.
  4. Выведите верхние k листьев и счетчиков (на самом деле это немного болезненно, потому что вам нужны родительские указатели, чтобы сопоставить каждый лист обратно со словом).

Поскольку размер кучи является постоянным, операции кучи - это O (1), поэтому шаг 3 - это O (n).

Куча также может поддерживаться динамически при построении дерева.

KWillets
источник
2

Ваш алгоритм даже не запускается за время ; вставка Θ ( n ) вещей в хеш-таблицу уже стоит время Ω ( n 2 ) (наихудший случай).О(NжурналN)Θ(N)Ω(N2)


То, что следует, неправильно ; Я оставляю это здесь пока в иллюстративных целях.

Следующий алгоритм выполняется в наихудшее время (при условии алфавита Σ постоянного размера), n количество символов в тексте.O(n)Σn

  1. Построить дерево суффиксов текста, например, с помощью алгоритма Укконена .

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

  2. Пройдите по дереву от корня и отрежьте все ветви в первом (белом) пространстве.

  3. Пройдите по дереву и отсортируйте список дочерних элементов каждого узла по количеству листьев.

  4. Выход дерева (листья слева направо) теперь представляет собой список всех слов, отсортированных по частоте.

Что касается времени выполнения:

  1. Алгоритм Укконена (в его расширенной форме) выполняется за время ; поддержание подсчета листьев не увеличивает thetas ; -Стоимость алгоритма.O(n)Θ
  2. Мы должны пройти один узел на символ каждого слова, встречающегося в тексте. Поскольку существует не более различных пар слово-символ, мы посещаем не более n узлов.nn
  3. Мы посещаем не более узлов (ср. 2) и проводим время O ( | Σ |log | Σ | ) = O ( 1 ) на узел.nO(|Σ|log|Σ|)=O(1)
  4. Мы можем получить доход (который, конечно, имеет размер ) простым обходом времени O ( n ) (ср. 2).O(n)O(n)

Более точные границы могут быть получены путем параметризации времени выполнения с количеством разных слов; если их мало, дерево мало после 2.

Рафаэль
источник
Алгоритм неверен (он не сортирует). Я больше не уверен, что линейное время возможно.
Рафаэль
1

Используйте хеш-таблицу (например, HashMap), чтобы собрать все слова и их частоты. Затем используйте сортировку подсчета, чтобы отсортировать слова в порядке убывания частоты. Поскольку все частоты являются целыми числами в диапазоне , сортировка при подсчете занимает время O ( n ) . Общее ожидаемое время работы O ( n ) , что более чем вероятно более чем достаточно для всех практических целей (если интервьюер не упомянул что-то, что было оставлено вне вашего вопроса). Не забудьте упомянуть, что это ожидаемое время выполнения, а не время выполнения в худшем случае .1..nO(n)O(n)

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

O(n)O(n)

DW
источник
Θ(n)Ω(n2)
Я не могу говорить за интервьюеров, но я не решаюсь использовать их неряшливость как оправдание для того же. Кроме того, этот сайт посвящен науке (как вы сами прокомментировали выше), а не о том, как вручную размахивать приемами программирования «как мне заплатят раньше».
Рафаэль
Пока это понимание становится явным, я в порядке с этим. Я видел здесь слишком много вопросов, которые были основаны на путанице, потому что какое-то скрытое «понимание» продвигало неверные идеи.
Рафаэль
0

Решение на основе хэш-таблицы

Ω(n2)n

nΩ(n)

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

Предполагается, что алгоритм хеширования является линейным по времени относительно количества символов.

Решение на основе сортировки Radix

В качестве альтернативы, если исходить из английского, поскольку длина слов хорошо известна, я бы вместо этого создал сетку и применил бы сортировку по основанию, которая O(kN)kNnkO(n)

2nnO(n)

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

Омер Икбал
источник
Θ(n)Θ(n)
O(n+n)O(n2)
(3) Какую бы хэш-функцию вы ни выбрали, я могу предложить ввод, где эта конкретная функция ухудшается. И выбор хеш-функции после знания ввода обычно не вариант. (И помните, что комментарий, к которому вы, вероятно, обращались,
касался
O(n2)
O(n2)O(1)Ω(1)O(1)O(1)