Храните самые большие 5000 номеров из потока чисел.

Учитывая следующую проблему:

"Храните самые большие 5000 номеров из потока чисел"

Решением, которое приходит в голову, является двоичное дерево поиска, поддерживающее подсчет количества узлов в дереве и ссылку на наименьший node, когда счет достигает 5000. Когда счетчик достигает 5000, каждый новый номер add можно сравнить с наименьшим элементом в дереве. Если больше, можно добавить новый номер, а затем самый маленький удаленный и новый наименьший расчет (который должен быть очень простым, если иметь предыдущий наименьший).

Моя забота об этом решении заключается в том, что бинарное дерево, естественно, будет искажаться (поскольку я только удаляю одну сторону).

Есть ли способ решить эту проблему, которая не создаст ужасно искаженного дерева?

В случае, если кто-то этого захочет, я включил псевдо-код для моего решения, находящегося ниже:

process(number)
{
  if (count == 5000 && number > smallest.Value)
  {
    addNode( root, number)
    smallest = deleteNodeAndGetNewSmallest ( root, smallest)
  }
}

deleteNodeAndGetNewSmallest( lastSmallest)
{
  if ( lastSmallest has parent)
  {
    if ( lastSmallest has right child)
    {
      smallest = getMin(lastSmallest.right)
      lastSmallest.parent.right = lastSmallest.right
    }
    else
    {
      smallest = lastSmallest.parent
    }
  }
  else 
  {
    smallest = getMin(lastSmallest.right)
    root = lastSmallest.right
  }
  count--
  return smallest
}

getMin( node)
{
  if (node has left)
    return getMin(node.left)
  else
    return node
}

add(number)
{
  //standard implementation of add for BST
  count++
}

Ответ 1

Простейшим решением для этого является поддержание min heap максимального размера 5000.

  • Каждый раз, когда приходит новое число - проверяйте, меньше ли куча, тогда 5000, если есть - добавьте его.
  • Если это не так - проверьте, меньше ли минимальный, чем новый элемент, и если это так, вытащите его и вставьте вместо него новый элемент.
  • Когда вы закончите - у вас есть куча, содержащая 5000 самых больших элементов.

Это решение O(nlogk) сложность, где n - количество элементов, а k - количество необходимых вам элементов (5000 в вашем случае).

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

Ответ 2

Использовать (минимум) приоритетную очередь. Добавьте каждый входящий элемент в очередь, и когда размер достигает 5000, удалите минимальный (верхний) элемент каждый раз, когда вы добавляете входящий элемент. Очередь будет содержать 5 000 самых больших элементов, и когда вход останавливается, просто удалите содержимое. Этот MinPQ также называется кучей, но это перегруженный термин. Вставки и удаления имеют значение log2 (N). Если N maxes out at 5000, это будет чуть больше 12 [log2 (4096) = 12] раз больше количества обрабатываемых вами элементов.

Отличным источником информации является Алгоритмы, (4-е издание) Роберта Седжуика и Кевина Уэйна. Существует отличный MOOC на coursera.org, основанный на этом тексте.