Частота слів із замовленням у складності O (n)


11

Під час співбесіди на посаду розробника Java мене попросили:

Напишіть функцію, яка займає два парами:

  1. a Рядок, що представляє текстовий документ і
  2. ціле число, що забезпечує кількість повернених елементів.

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

Далі я відповів (у псевдокоді), що це не час а швидше через сортування. Я не можу зрозуміти, як це зробити час. O(n)O(nlogn)O(n)

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

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

return wordFrequencyMap.sortByValue.keys

Хтось знає чи може хтось мені підказати?


1
Використовуйте хеш-таблицю.
Yuval Filmus

Використання хештелю не вирішує проблему. Крім того, хештел - це застаріла Java.
користувач2712937

Таблиці хешу зазвичай є хитрістю, щоб знизити складність від до . Навіть якщо вони є застарілою Java, що б це не означало. Я не перевіряв конкретний випадок, тож ви можете мати рацію. O(nlogn)O(n)
Yuval Filmus

@YuvalFilmus. Дякую, але хеш-таблиця майже однакова як хеш-карта, яку я вже використовую (основна різниця між 2-ма структурою даних - це синхронізація, яка тут не застосовується). Журнал (n) в шахті походить від сортування значень на хеш-карті.
користувач2712937

3
До речі, цей сайт орієнтований на концепції та алгоритми, а не на код. Тому, як правило, ми попросимо вас видалити код Java та дати концептуальний опис вашого підходу (можливо, якщо буде потрібно стислий псевдокод високого рівня). Також на цьому веб-сайті актуальним питанням є, які структури даних та алгоритми використовувати; специфічний Java API не є темою для цього сайту (але ви можете запитати про це на StackOverflow), і аналогічно, чи Hashtableце застаріла Java чи ні, насправді не має значення для цілей цього сайту.
DW

Відповіді:


10

Я пропоную варіацію підрахунку розподілу:

  1. Прочитайте текст і вставіть усе слово, яке зустрічається у трійці , підтримуючи в кожному вузлі кількість, як часто трапляється слово, представлене цим вузлом. Крім того, слідкуйте за найвищим числом слів maxWordCound. -O(n)
  2. Ініціалізуйте масив розміру maxWordCount. Тип запису - це списки рядків. - , оскільки кількість не може бути більшою.O(n)
  3. Пройдіть трійку і для кожного вузла додайте відповідний рядок до запису масиву, зазначеного підрахунком. - , оскільки загальна довжина струн обмежена .O(n)n
  4. Прокладіть масив у порядку зменшення та виведіть потрібну кількість рядків. - , оскільки це обмежено як розміром, так і кількістю даних у масиві.O(n)

Ви, ймовірно, можете замінити трійку іншими структурами даних на першій фазі.


+1, хоча я не впевнений у цьому. Це O (n), оскільки кількість повернених слів обмежується n, кількістю символів, але це запитання задається? Або результат, незалежний від кількості повернених слів?
Нікос М.

@NikosM. Він є ; - загальна найгірша верхня межа щодо кількості повернених слів, а не припущення. n
Рафаель

@Raphael, так правильно, я думаю про це, оскільки в інтерв'ю йому було запропоновано можливі хитрощі в питанні ..
Нікос М.

Мені цікаво, чи існує просторовий алгоритм лінійного часу.
saadtaame

3
@saadtaame, так, це цікаве питання. Можливо, варто окремо поставити запитання як окреме питання. Це не просто ефективність простору; рішення трие також є інтенсивним покажчиком, що може зробити його повільніше на практиці (враховуючи, як працює ієрархія пам'яті в реальних машинах). "Ефективність" відрізняється від найгіршого часу роботи. Це не є незвичайним для чистого алгоритму часу , щоб перемогти алгоритм, що інтенсивно працює на покажчик часу, тому це питання вже, здається, виключає деякі потенційні алгоритми, які можуть бути кращим вибором на практиці. O(nlgn)O(n)
DW

3

Збір підрахунків подій дорівнює O (n), тому фокус насправді є лише знаходженням вершин k подій k.

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

Припустимо, що k є другим параметром вище, і що це константа в заяві проблеми (здається):

  1. Побудуйте трійку слів із кількістю підрахунків на кожному вузлі.
  2. Ініціалізуйте купу розміром k.
  3. Прокладіть трійку та min-зонд / вставити кожну пару (лист, кількість подій) у верхній купі.
  4. Виведіть літери k та відлічіть верхню частину (це насправді біль, тому що вам потрібні батьківські вказівники для відображення кожного листа назад до слова).

Оскільки розмір купи є постійним, операції купи - це O (1), тому крок 3 - O (n).

Купу також можна динамічно підтримувати, будуючи трійку.


2

Ваш алгоритм навіть не працює в часі ; вставлення речей у хешблей коштує часу вже (в гіршому випадку).O(nlogn)Θ(n)Ω(n2)


Далі випливає неправильно ; Я поки що залишаю його тут для ілюстративних цілей.

Наступний алгоритм працює у гіршому випадку (припускаючи алфавіт постійного розміру), кількість символів у тексті.O(n)Σn

  1. Побудуйте суфіксне дерево тексту, наприклад, за допомогою алгоритму Укконена .

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

  2. Відірвіть дерево від кореня і обріжте всі гілки на першому (білому) просторі.

  3. Обведіть дерево та сортуйте список дітей кожного вузла за їх кількістю листків.

  4. Врожайність дерева (листя зліва направо) тепер - це список усіх слів, відсортований за частотою.

Щодо часу виконання:

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

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


Алгоритм невірний (він не сортує). Я вже не впевнений, що лінійний час навіть можливий.
Рафаель

1

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

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

Або, якщо ви хочете відіграти його трохи безпечніше, перш ніж давати відповідь, спершу запитайте: «Вас хвилює різниця між очікуваним часом та найгіршим часом часу?». Потім відповідно адаптуйте свою відповідь. Будьте готові до того, що інтерв'юер запитає , як би ви вибрали на практиці. (Якщо так, забийте! Це питання, яке ви повинні мати можливість вийти з бального парку.)O(n)O(n)


Зберігання Θ(n) речі в хештебі займає Ω(n2)час у гіршому випадку вже.
Рафаель

Я не можу говорити за інтерв'юерів, але я вагаюся використовувати їх неохайність як привід для того ж самого. Крім того, на цьому веб-сайті йдеться про науку (як ви самі прокоментували вище), а не про розмахування руками "як мені швидше заплатять" про прийоми програмування.
Рафаель

Поки це розуміння стане явним, я з цим добре. Я бачив тут занадто багато питань, які були засновані в замішанні, оскільки якесь неявне "розуміння" сприяло неправильним ідеям.
Рафаель

0

Розчин на основі хешбела

Не впевнений, чому хештейн ускладнює складність Ω(n2) якщо n- кількість символів (не слів).

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

Зіткнення в хештейлі, безумовно, є проблемою, і залежно від того, наскільки великий був оригінальний хештель та наскільки хороший алгоритм хешування, можна було б наблизитися до O(1) для вставки та зберігання рахунків і таким чином O(n)для алгоритму, хоча за рахунок пам’яті. Однак я все ще не можу оцінити, як можна стверджувати про найгірший випадокO(n2) якщо n - кількість символів.

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

Рішення на основі сортування Radix

Крім того, якщо припустити англійську мову, оскільки довжина слів добре відома, я б замість цього створив сітку та застосував сортинг radix, який є O(kN) де k була б максимальна довжина слова в англійській мові та N- загальна кількість слів. Даноn - кількість символів у документі та k є постійною, асимптотично це сума O(n).

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

Кілька найдовших слів англійською мовою є смішно довгими , але тоді можна обмежити довжину слова на розумну кількість (наприклад, 30 чи менших) та обрізати слова, приймаючи похибки, які можуть бути пов'язані з цим.


(1) Оскільки в більшості текстів максимальна довжина слів обмежена постійною, кількість слів становить Θ(n)так само. (2) Залежно від функції хешу, можливо, неможливо обчислити хеш під час читання слова. (3) У гіршому випадку всі слова мають хеш-пам'ять на одне і те саме місце в таблиці, роблячи вставку та пошукΘ(n).
FrankW

Привіт, FrankW. (2) Я заявляю, що ми можемо вибрати функцію (тобто котиться хеш), яку ми можемо обчислити на льоту. Навіть якщо ні, загальна складність не змінюється до тих пір, поки хешування буде лінійним часом, адже читання та хеширование було бO(n+n)операції. (3) Звичайно, але це знову залежить від вибору алгоритму. Існує багато алгоритмів, які значно краще, якщо слова різні. Для того ж слова ви просто збільшуєте кількість за один запис. Як аналогія, коли мені доводиться вибирати алгоритм сортування, найгірший випадок може бутиO(n2)але я зазвичай вибираю краще :-)
Омер Ікбал

(3) Яку б хеш-функцію ви не вибрали, я можу запропонувати інформацію, де ця конкретна функція деградує. І вибір хеш-функції після знання вводу зазвичай не є варіантом. (І пам’ятайте, що коментар, до якого ви ймовірно
зверталися, стосувався

Чому веде хеш-таблиця O(n2)найгірша складність? Це тому, що в принципі найгірший час роботи хештелю дуже поганий. На практиці цей гірший випадок майже ніколи не виникає (особливо якщо правильно вибрати хеш-функцію, з рандомізацією та іншими методами), і навіть можна довести теореми, щоб виправдати, чому це так, але якщо це питання про асимптотичну складність такі практичні міркування, напевно, виходять у вікно (або, принаймні, це аргумент, який ви можете почути).
DW

Звичайні вставки хеш-таблиць є O(n2)тому що зіткнення вимагає розміщення предмета в іншому місці. Тут нам не потрібно вставляти дублікати. 1) Це ж слово повторюється: тоді вгору підрахунок, це гарантованоO(1)плюс час хешування 2) Різні слова той самий хеш: тут виникає питання про те, наскільки хороший / поганий хеш, і якщо розмір таблиці просто занадто малий. Я згоденΩ(1), але залежно від вибору я також зазначив, що "можна було б наблизитися доO(1) для вставки та зберігання рахунків ". Ми могли б обговорити, який розмір та функції таблиці можуть нас наблизити O(1).
Омер Ікбал
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.