Алгоритм Хаффмана за O(n) — различия между версиями
Ильнар (обсуждение | вклад) |
Ильнар (обсуждение | вклад) |
||
Строка 92: | Строка 92: | ||
| Массив 2 || 2 || 4 || 6 || 11 || <tex>\infty</tex> | | Массив 2 || 2 || 4 || 6 || 11 || <tex>\infty</tex> | ||
|} | |} | ||
+ | |||
+ | ==Псевдокод== | ||
+ | '''int''' HuffmanCoding(a: '''int[0..n]'''): | ||
+ | b: '''int[0..n]''' | ||
+ | i, j, inf, ans: '''int''' ''<font color=green>// i, j {{---}} указатели в массивах, inf {{---}} большое число</font>'' | ||
+ | '''for''' k = 0 '''to''' n | ||
+ | b[k] = inf | ||
+ | '''for''' k = 0 '''to''' n - 1 | ||
+ | '''if''' a[i] + a[i + 1] <= a[i] + b[j] && a[i] + a[i + 1] <= b[j] + b[j + 1] | ||
+ | b[k] = a[i] + a[i + 1] | ||
+ | ans += b[k] | ||
+ | i += 2 | ||
+ | '''continue''' | ||
+ | '''if''' a[i] + b[j] <= a[i] + a[i + 1] && a[i] + b[j] <= b[j] + b[j + 1] | ||
+ | b[k] = a[i] + b[j] | ||
+ | ans += b[k] | ||
+ | i++ | ||
+ | j++ | ||
+ | '''continue''' | ||
+ | '''if''' b[j] + b[j + 1] <= a[i] + a[i + 1] && b[j] + b[j + 1] <= a[i] + b[j] | ||
+ | b[k] = b[j] + b[j + 1] | ||
+ | ans += b[k] | ||
+ | j += 2 | ||
+ | '''return''' ans | ||
+ | ==См. также== | ||
+ | *[[Алгоритм Хаффмана]] | ||
+ | *[[Оптимальное хранение словаря в алгоритме Хаффмана]] |
Версия 17:36, 12 января 2015
Задача: |
Пусть у нас есть отсортированный по возрастанию алфавит | , . Где — число вхождений символа в строку. Требуется построить код Хаффмана за .
Содержание
Описание алгоритма
Eсли массив не отсортирован, то это можно сделать, например, цифровой сортировкой за , что не ухудшит асимптотику.
Идея алгоритма заключается в том, чтобы создать такую очередь с приоритетами, из которой можно было бы доставать два минимума за , после чего в эту же очередь с приоритетами положить их сумму за . У нас уже есть массив с отсортированными частотами, теперь давайте заведем второй массив, в котором мы будем хранить суммы. Несложно заметить, что в этом массиве элементы тоже будут идти по неубыванию. Допустим, что на каком-то шаге сумма получилась меньше чем предыдущая, но это противоречит тому, что на каждом шаге мы выбираем два минимальных. На каждой итерации мы будет выбирать два минимума из четырех элементов (первые 2 элемента первого массива и первые 2 элемента второго массива). Теперь рассмотрим одну итерацию подробнее.
У нас есть три варианта возможных пар минимумов :
- Оба элемента из первого массива.
- Первый элемент первого массива и первый элемент второго массива.
- Два первых элемента второго массива.
Во всех случаях мы дописываем сумму в конец второго массива и передвигаем указатели в массивах на еще не использованные элементы.
На каждом шаге количество элементов уменьшается ровно на один, а минимум из 4-х элементов мы выбираем за константное время, следовательно, программа делает ровно
итераций.Пример
Для примера возьмем строку "абракадабра".
— указатели на первые неиспользованные элементы в массиве 1 и 2, соответственно.
Буква | д | к | б | р | а |
---|---|---|---|---|---|
Массив 1 | 1 | 1 | 2 | 2 | 5 |
Массив 2 |
На первом шаге два минимальных элемента - это первые две ячейки первого массива. Их сумму сохраняем во второй массив.
Буква | д | к | б | р | а |
---|---|---|---|---|---|
Массив 1 | 1 | 1 | 2 | 2 | 5 |
дк | |||||
---|---|---|---|---|---|
Массив 2 | 2 |
На втором шаге снова суммируются первые две ячейки первого массива(нам все равно что взять, первый элемент второго массива или второй элемент первого).
Буква | д | к | б | р | а |
---|---|---|---|---|---|
Массив 1 | 1 | 1 | 2 | 2 | 5 |
дк | бр | ||||
---|---|---|---|---|---|
Массив 2 | 2 | 4 |
На третьем шаге два минимальных элемента - это первые две ячейки второго массива.
Буква | д | к | б | р | а |
---|---|---|---|---|---|
Массив 1 | 1 | 1 | 2 | 2 | 5 |
дк | бр | дкбр | |||
---|---|---|---|---|---|
Массив 2 | 2 | 4 | 6 |
На четвертом шаге складываются две оставшиеся ячейки.
Буква | д | к | б | р | а |
---|---|---|---|---|---|
Массив 1 | 1 | 1 | 2 | 2 | 5 |
дк | бр | дкбр | адкбр | ||
---|---|---|---|---|---|
Массив 2 | 2 | 4 | 6 | 11 |
Псевдокод
int HuffmanCoding(a: int[0..n]): b: int[0..n] i, j, inf, ans: int // i, j — указатели в массивах, inf — большое число for k = 0 to n b[k] = inf for k = 0 to n - 1 if a[i] + a[i + 1] <= a[i] + b[j] && a[i] + a[i + 1] <= b[j] + b[j + 1] b[k] = a[i] + a[i + 1] ans += b[k] i += 2 continue if a[i] + b[j] <= a[i] + a[i + 1] && a[i] + b[j] <= b[j] + b[j + 1] b[k] = a[i] + b[j] ans += b[k] i++ j++ continue if b[j] + b[j + 1] <= a[i] + a[i + 1] && b[j] + b[j + 1] <= a[i] + b[j] b[k] = b[j] + b[j + 1] ans += b[k] j += 2 return ans