【问题标题】:What is the correct term for a "coalescing" event queue?“合并”事件队列的正确术语是什么?
【发布时间】:2019-09-27 09:42:46
【问题描述】:

假设我有一个“事件”对象队列,其中每个对象都有一个“事件类型”和某种数据字段。在 C++ 中可能是这样的:

enum class EventType
{
    A,
    B,
    C
};

struct Event
{
     EventType type;
     std::string message;
};

比如说,我想要一个包含这些事件的队列,并且我希望队列具有以下属性:

  • 当一个事件被推送到队列中时,相同EventType 的任何现有事件都将被删除。因此,当从队列中弹出一个事件时,它始终是其EventType 的最新最近添加实例
  • 不同 EventType 的事件按照它们被推送的顺序弹出。 (pop() 不会期望任何参数)。

因此队列对于EventLIFO 对于EventType 是FIFO。

示例:

以下事件被推送到队列中(字母为事件类型,数字为实例号)

A1 A2 C1 C2 C3 B1 B2 A3

如果 pop() 被调用 3 次,则按顺序返回的项目将是

C3 B2 A3

这种类型的数据结构有名称吗?有实现的例子吗?

【问题讨论】:

  • 那么pop需要指定事件类型吗?
  • 我可能遗漏了一些东西,但您的要求不意味着队列中恰好有 0 或 1 个给定类型的事件吗?你如何想象函数 Pop() 的签名是什么样的?您是否指定了事件类型?
  • 听起来像是优先队列和驱逐队列的组合(当然还有自定义逻辑)
  • Qt documentation 谈论“压缩”事件。但目前尚不清楚这是否也适用于非相邻事件。
  • boost::bimap<unordered_set<EventType>, list_of<Event>> 可能是一个很好的基础数据结构。

标签: algorithm data-structures


【解决方案1】:

免责声明:非常高效的数据结构设计与领域(架构)和数据上下文密切相关。以下解决方案旨在对一般情况在理论上有效。有关详细信息,请参阅最终注意事项。


结构设计

这个想法很简单。

保留一个代表队列底层数据结构

每次插入新事件时,检查类型是否已经存在。在这种情况下,我们可以删除该元素。通过这种方式,我们维护了第一个属性(每种类型只有一个实例,这意味着我们总是弹出该类型的最新实例)。

最后,我们将新实例插入到队列的末尾。因此,我们保留第二个属性。


一个简单实现的例子

#include <algorithm>
#include <list>
#include <utility>

class QueueWithAGoodName {
 public:
  using reference = Event&;

  void push(Event event) {
    if (const auto finder =
            std::find_if(queue_.begin(),
                         queue_.end(),
                         [eventType = event.type](const Event& event) {
                           return event.type == eventType;
                         });
        finder != queue_.end()) {
      queue_.erase(finder);
    }
    queue_.push_back(std::move(event));
  }

  reference front() { return queue_.front(); }

  void pop() { queue_.pop_front(); }

 private:
  std::list<Event> queue_;
};

Live Example


考虑与分析

  • 在我的示例中,我使用了std::list 作为基础数据结构。事实上,std::list 非常方便,以防您经常在“中间”删除。此外,删除后,迭代器不会失效。如果我们想改进队列,这是一个很好的属性。

  • QueueWithAGoodName::push 具有运行时间复杂度:O(N)(因为我们应用线性扫描来查找类型)。其中N 是队列的大小。

  • QueueWithAGoodName::popQueueWithAGoodName::frontO(1)

进一步考虑

  • 对于这种类型的数据结构,有一个重要的属性。每种类型都有一个实例。这意味着N(队列的大小)的上限为EventTypes 的数量。
  • 如果事件类型的数量有限,可以应用几个考虑因素,因为(对于常见架构)运行时复杂性O(N) 可以近似为O(1)。在这种情况下,由于缓存,std::vector 作为底层数据结构可能更有效。
  • 在我报告的简单示例中,pushO(N)。如果您有几种类型的EventTypes,您可以使数据结构在推送操作中更高效。简而言之,可以使用辅助std::unordered_map&lt;EventType, list::iterator&gt;(即hash map)来避免线性搜索。擦除后,std::list 不会使迭代器失效,因此哈希表保持一致。

【讨论】:

  • 谢谢。它很好地回答了要求参考实现的问题部分。我们能为这种类型的队列起一个好听的名字吗? TypeEvictingFIFO ?
猜你喜欢
  • 2010-10-29
  • 2011-06-23
  • 2022-07-21
  • 2016-01-14
  • 2015-05-29
  • 2011-12-28
  • 1970-01-01
  • 2016-04-17
  • 2013-01-12
相关资源
最近更新 更多