В этой статье в простой форме описывается структура данных кучи и некоторые варианты использования структуры данных кучи. Это подходит для начинающих или разработчиков, чтобы найти вопрос интервью, связанный с кучей.

Мы продолжим с некоторыми вариантами использования или вопросами интервью, связанными с кучей.

Слияние K отсортированных массивов (разного размера)

Это известный вопрос интервью.

input = {1, 3, 9, 12}, {2, 6, 10, 14, 15}, {4, 7, 8, 13}, {5, 9, 11}
output = {1,2,3,4,5,6,7,8,9,9,10,11,12,13,14,15}

Решение 1. Наивный подход

Мы создаем массив для хранения результата, помещаем один за другим элементы массива в этот результат и используем любой алгоритм сортировки для сортировки результатов. этот подход будет иметь сложность O (NlogN), где N - общий элемент массивов списков.

Решение 2. Используйте приоритетную очередь

Идея в том, что мы используем приоритетную очередь для поддержания порядка элементов в массиве K.

input = {1, 3, 9, 12}, {2, 6, 10, 14, 15}, {4, 7, 8, 13}, {5, 9, 11}
Step 1 : Create Queue with size = k . We interator through K Array and put their first element to the Queue . We got 
                   Queue = {1,2,4,5} .
Step 2 : We poll the minimum element out of the queue and put to result .
                   Queue = {2,4,5}
                   Result = {1}
Step 3 : Put the second element of first array to the queue
                   Queue = {2,3,4,5}
                   Result = {1}
Step 4 : Repeat Step 2 
                   Queue = {3,4,5}
                   Result = {1,2}
Step 5 : Repeat Step 3 but change the first array to second array
                   Queue = {3,4,5,6}
                   Result = {1,2}
Step xxx : ... Repeat Step 2 and 3 but change the element index and array index . I think you already know how to play the game .

Поскольку мы должны перебрать все элементы, поэтому мы получили O (N), но каждый элемент, который нам нужен, помещается () в приоритетную очередь, это действие занимает O (LogK), где K - общий массив. Таким образом, это решение имеет сложность O (NLogK).

Кодирование Хаффмана

Кодирование Хаффмана — это алгоритм сжатия данных. Он основан на частом появлении символа или элемента в тексте или списке.

Пример: kaangnnggg

Номер символа по порядку

g -> 4 | n->3 | a->2 | h->1| k->1

это означает, что символ g появляется в тексте 4 раза.

Символ ASCII в 8-битной кодировке ASCII составляет 8 бит.

Символ Unicode в кодировке UTF-32 всегда имеет длину 32 бита.

…..

Пример: наш персонаж os занимает 8 бит памяти. Таким образом, с помощью g с потерянными 32 битами можно сохранить 4 символа g. Если мы сможем преобразовать g, станет 1 бит. поэтому мы можем сохранить 32–4 = 28 бит

g = 01100111-> g = 0;

n = 01101110 -> n = 1;

a = 01100001 -> a = 11;

h = 01101000 -> h = 100;

k = 01101011 -> k = 101;

поэтому khaangnnggg будет закодирован как 101|100|11|11|1|0|1|1|0|0
0 . Но настоящая проблема заключается в расшифровке. если мы декодируем биты 1011001111101100 . Это приведет ко многим результатам.

1|0|1|100|1|1|11|0|1|100 -> нгнхннагнх

и так далее

У нас есть правило, согласно которому ни один код не является префиксом другого кода. Под кодом мы подразумеваем биты, используемые для определенного символа. С приведенным выше примером. n является префиксом a или h или k . мы можем исправить приведенный выше пример с помощью

g = 0

n = 10

a = 110

h = 1110

k = 1111

поэтому khaangnnggg будет закодирован как 1111|1110|110|110|10|0|10|10|0|0|0 . Мы можем декодировать его с помощью приведенной выше карты кода, не думая о двусмысленности.

Как создать карту кода

Мы используем двоичное дерево узлов, когда левый дочерний элемент представляет бит «0», а правый дочерний элемент представляет бит «1».

Идеально использовать очередь с приоритетами для построения дерева Хаффмана, где узлу с наименьшей частотой отдается наивысший приоритет.

Ввод: карта‹char,int› = {g=4,n=3,a=2,h=1,k=1}

Шаг 1. Преобразуйте каждый элемент карты в узел дерева.

Шаг 2. Поместите все узлы дерева в приоритетную очередь, отсортированную по частоте появления.

pq = {k=1,h=1,a=2,n=3,g=4}

Шаг 3. Извлеките 2 узла дерева из очереди.

h=1 , k=1

pq = {a=2,n=3,g=4}

Создайте новый внутренний узел из узла дерева выше 2, и он также является их родителем, внутренний узел не представляет ни одного символа, но имеет свой вес, который рассчитывается по сумме весов узла дерева выше 2.

внутренний (вес) = h(вес) + k(вес) = 1 + 1 = 2

Мы связываем или указываем левый дочерний элемент внутреннего узла на k и правый дочерний элемент внутреннего узла на h.

Шаг 4. Мы помещаем внутреннее значение в очередь.

pq = {внутреннее = 2, a = 2, n = 3, g = 4}

замените шаг 3 и шаг 4, пока queue.size =1 ;

Шаг 5: вытащите последний элемент из очереди. Это корень нашего дерева. начинаем делать код для каждого листа. С левым дочерним элементом мы помечаем его битом «0» и битом «1» для правого дочернего элемента.

мы путешествуем от корня к листовому узлу и собираем их код

g = 0

n = 10

a = 110

h = 1110

k = 1111

полный исходник можно получить здесь