Куча
K самых частых элементов
Найти k элементов с максимальной частотой
Постановка задачи
Описание (LeetCode - 347. Top K Frequent Elements)
Дан целочисленный массив nums и число k.
Нужно вернуть k самых частых элементов. Ответ можно вернуть в любом порядке.
Пример 1:
nums = [1, 1, 1, 2, 2, 3]
k = 2
Результат:
[1, 2]
Пояснение:
Число 1 встречается 3 раза, число 2 встречается 2 раза, число 3
встречается 1 раз. Два самых частых элемента - 1 и 2.
Пример 2:
nums = [1]
k = 1
Результат:
[1]
Ограничения:
1 <= nums.length <= 10⁵-10⁴ <= nums[i] <= 10⁴kнаходится в диапазоне от1до количества различных элементов в массиве- ответ гарантированно уникален без учета порядка элементов
Решение
В этой задаче нужно решить две подзадачи:
- Посчитать частоту каждого элемента (с помощью хеш-таблицы).
- Выбрать
kэлементов с наибольшей частотой (с помощью кучи).
Идея решения:
Сначала считаем частоты всех чисел с помощью хеш-таблицы. Затем поддерживаем
минимальную кучу размера k, где каждый элемент хранит пару (число, частота),
упорядоченную по частоте.
Вершина такой кучи - элемент с наименьшей частотой среди текущих кандидатов.
Если в куче стало больше k элементов, удаляем вершину. Так в куче всегда
остаются только k самых частых элементов из всех просмотренных.
Почему именно минимальная куча, а не максимальная?
Если использовать максимальную кучу, в нее придется положить все уникальные
элементы массива, а затем извлечь максимум k раз. Это потребует памяти под
все различные числа. Минимальная куча размера k хранит только лучших
кандидатов и экономит память, когда уникальных чисел много.
Основные шаги:
- Считаем частоту каждого числа в хеш-таблице
frequency. - Создаем минимальную кучу, упорядоченную по частоте.
- Проходим по парам
(число, частота)из хеш-таблицы:- добавляем пару в кучу;
- если размер кучи больше
k, удаляем элемент с наименьшей частотой.
- Извлекаем оставшиеся числа из кучи и возвращаем результат.
Рассмотрим пример:
nums = [1, 1, 1, 2, 2, 3]
k = 2
Частоты:
1 -> 3
2 -> 2
3 -> 1
- Добавляем
1с частотой3. Куча:[(1, 3)].
- Добавляем
2с частотой2. Куча:[(2, 2), (1, 3)].
- Добавляем
3с частотой1. Размер стал3, а нам нужно хранить не более2элементов. Удаляем вершину с минимальной частотой - пару(3, 1).
В куче остались 1 и 2, это и есть ответ.
Если на интервью попросят решение быстрее O(n log k), можно предложить
карманную сортировку (bucket sort) по частотам за O(n). Но в этой теме мы
разбираем именно подход с кучей.
Оценка сложности
Временная сложность
Подсчет частот занимает O(n). Пусть m - количество различных элементов
в массиве (m <= n). Для каждого из них мы выполняем добавление в кучу и,
возможно, удаление за O(log k).
Итоговая временная сложность: O(n + m log k). В худшем случае m = n, поэтому
сложность составляет O(n log k).
Пространственная сложность
Хеш-таблица хранит до m различных чисел, а куча хранит не более k элементов.
Итоговая дополнительная память: O(m + k), что в худшем случае равно O(n).
Код решения
Приведем код решения.
import "github.com/emirpasic/gods/trees/binaryheap"
type Pair struct {
num int
freq int
}
func topKFrequent(nums []int, k int) []int {
frequency := map[int]int{}
for _, num := range nums {
frequency[num]++
}
heap := binaryheap.NewWith(func(a, b interface{}) int {
return a.(Pair).freq - b.(Pair).freq
})
for num, freq := range frequency {
heap.Push(Pair{num: num, freq: freq})
if heap.Size() > k {
heap.Pop()
}
}
result := []int{}
for !heap.Empty() {
val, _ := heap.Pop()
result = append(result, val.(Pair).num)
}
return result
}Итоги
Задача Top K Frequent Elements объединяет хеш-таблицу для подсчета частот и
кучу для выбора лучших кандидатов.
- Суть алгоритма: сначала считаем частоты в хеш-таблице, затем поддерживаем
минимальную кучу размера
kпо этим частотам. - Эффективность:
- Временная сложность:
O(n + m log k), гдеm- количество различных элементов. - Пространственная сложность:
O(m + k).
- Временная сложность:
- Граничные случаи:
k = 1: в куче останется один самый частый элемент.- Все элементы уникальны: ответом будут любые
kэлементов, так как частота каждого равна1.
- Оптимальное решение: мы не сортируем все элементы по частоте. Куча
размера
kпозволяет сразу отбрасывать кандидатов с меньшей частотой.