Очередь шаблонов команд, упорядоченная по времени?

Я изменяю некоторый код, чтобы использовать шаблон команды, и буду хранить объекты команд в очереди. Команды нужно будет выполнять в определенное время, поэтому я буду перебирать список раз в секунду, чтобы найти команды для выполнения.

С каждым объектом команды будет связано время, и я буду сравнивать это время с текущим временем (в пределах небольшого порога). Поэтому мне нужно будет удалить объект команды из списка, если его время совпадает, а затем выполнить его. Как правило, в любой момент времени будет менее 10 команд. Какую структуру данных коллекции следует использовать и как удалять объекты команд при повторении списка?


person User    schedule 18.04.2012    source источник


Ответы (2)


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

C++ моделирует приоритетную очередь как адаптер контейнера priority_queue. Это означает, что внутри него есть какой-то другой контейнер, который выполняет фактическое хранение. Обычно это контейнер vector или deque. (Требуются итераторы произвольного доступа.) По умолчанию используется значение vector. Итак, вы можете просто объявить:

std::priority_queue<T, vector<T>, Compare> queue;

где T — ваш элемент, а Compare — функция, которая сравнивает два элемента T и возвращает true, если первый имеет более низкий приоритет, чем второй. Если вы определили operator < для вашего типа T, все становится еще проще:

std::priority_queue<T> queue;
  • Чтобы поставить элемент в очередь: queue.push(item);
  • Чтобы получить элемент с наивысшим приоритетом: queue.top()
  • Чтобы удалить верхний элемент очереди: queue.pop();

Обратите внимание, что pop() не возвращает удаленный элемент; он просто удаляет и уничтожает его.

person Mike DeSimone    schedule 18.04.2012
comment
Итак, я предполагаю, что это будет связано с использованием времени в качестве приоритета? Поскольку мне нужно запускать команды в определенное время дня (например, 11:27:19), нужно ли мне продолжать выталкивать команды до тех пор, пока я не вытащу ту, которая истекла в текущее время, а затем поставлю эту команду обратно в очередь? - person User; 19.04.2012
comment
Нет, приоритетная очередь сохраняет для вас порядок, пока работает ваша функция Compare. Поэтому, когда вы берете top(), вы знаете, что это предмет с наивысшим приоритетом. Просто убедитесь, что Compare(a, b) или a < b возвращают true, если b предшествует a. Таким образом, вы просто посмотрите на top(), выполните его или нет, и только pop(), если вы его выполнили. Повторная установка не требуется. - person Mike DeSimone; 19.04.2012
comment
Обратите внимание, что обычное определение < для времени не будет работать, поскольку оно определяет раньше как меньше, чем позже. Поэтому вы, вероятно, просто захотите определить свой собственный < для своего класса T. - person Mike DeSimone; 19.04.2012
comment
А, понятно. Как вы чувствуете, что это сравнивается с итерацией по вектору (поскольку список будет небольшим)? - person User; 19.04.2012
comment
@user, поскольку список очень маленький, вы, вероятно, можете использовать любую структуру данных, которую пожелаете. Это для вашего удобства! Если вы собираетесь, например, перечислить отложенные команды в порядке их выполнения, то с простым вектором вам придется их отсортировать. И с решением priority_queue или map/multimap, которое я разместил ниже, вы готовы к работе. И вы можете следить за одним элементом — один в верхней части структуры, чтобы решить, должна ли выполняться какая-либо команда. Нет необходимости повторять. - person doc; 19.04.2012
comment
Я бы рекомендовал vector, потому что он будет быстрее, чем deque для небольших списков, и код будет проще. - person Mike DeSimone; 19.04.2012
comment
@MikeDeSimone: Когда вы говорите использовать vector, вы имеете в виду vector как базовую коллекцию priority_queue или просто vector? - person User; 19.04.2012
comment
vector в качестве базовой коллекции priority_queue. Как я и написал в ответе. - person Mike DeSimone; 19.04.2012
comment
Для тех, кто ломает голову, как я, это требует #include <queue> - person Jonathan Rys; 06.09.2018

Альтернативой решению Майка является использование упорядоченной карты, которая в случае STL представляет собой просто map. Вы можете сохранить время как ключ и команду как значение. В зависимости от вашего случая это может быть удобнее, чем очередь.

Если одновременно разрешено выполнение двух команд, то следует использовать multimap.

multimap<time_t, Command> schedule;

schedule.insert(pair<time_t, Command>(123456, formatHDDCommand));
person doc    schedule 18.04.2012
comment
Проблема с решением на основе карты заключается в том, что мне нужно допустить небольшое проскальзывание, поэтому, если текущее время на 1 или 2 секунды превышает целевое время, а команда все еще не выполнена, мне все еще нужно ее выполнить. - person User; 19.04.2012
comment
@Пользователь, конечно! Вы не должны искать на карте точное время. Вместо этого у вас есть два других пути для использования multimap. Во-первых, вы можете выбрать итератор begin или end (в зависимости от представления времени) и сравнить его время с текущим временем. Если текущее время больше, вы выполняете команду. Повторяйте этот шаг по мере необходимости. Во-вторых, вы можете использовать функцию find() с текущим временем в качестве аргумента и выполнять итерацию, начиная с итератора, возвращаемого find(). find() будет отсекать команды, время которых еще не пришло. - person doc; 19.04.2012
comment
А, понятно, я думал, вы предлагаете точное соответствие ключей, но вы используете карту из-за ее упорядоченного свойства. - person User; 19.04.2012
comment
Не используйте end(); по определению он не указывает на допустимый элемент. Вместо этого используйте rbegin(). Кроме того, по определению проблемы вам не нужно будет использовать find(). Ты будешь . В целом, это гораздо более сложное решение, чем priority_queue, и вам действительно не нужна функция времени поиска O(logN) map. Вы не выполняете произвольный поиск; вся ваша работа будет с самым ранним событием, так как вы хотите обрабатывать их по порядку. - person Mike DeSimone; 19.04.2012
comment
@MikeDeSimone Мне тоже больше нравится твой priority_queue. Но я предполагаю, что в некоторых случаях карта/мультикарта может быть более удобной. Может быть, время выполнения будет привязано к какой-то другой структуре данных или предсказано? Тогда быстрый поиск точных пар ключ => значение имеет смысл и оправдан. Кроме того, грануляция времени может быть достаточно большой, чтобы оправдать мультикарту. Например, в человеческом масштабе, если время представлено часами, то запрос на выполнение команд в указанный час имеет смысл, и мультикарта кажется естественным выбором. Это человеческий масштаб, но то же самое может быть верно и в меньших масштабах. - person doc; 19.04.2012
comment
priority_queue имеет больше смысла и для вашего примера с часами. Шкала времени не имеет значения. То, что вы делаете со структурой, имеет значение. Пока у вас есть коллекция элементов с приоритетами и вам нужен быстрый доступ к элементу с наивысшим приоритетом, вам нужна очередь с приоритетом. Если вам нужен произвольный доступ, то, конечно, вам нужна карта, но это совсем другая проблема, и здесь речь не шла об этом. - person Mike DeSimone; 19.04.2012
comment
@MikeDeSimone вся моя мысль заключалась в том, что шкала времени не имеет значения. Что важно, так это характер проблемы, поэтому, если команды нужно сгруппировать только в (несколько) временных интервалов, то priorioty_queue не обязательно имеет для меня больше смысла. Но это был только один пример. Я не говорю, что map/multimap лучше; Я только предполагаю, что в некоторых случаях это может подойти. То, что мы видим в вопросе, является скорее выдержкой из более крупной проблемы, а не самой проблемой. Кстати, доступ к верхнему элементу map\multimap такой же быстрый, как и в priority_queue - O(1). - person doc; 20.04.2012