Algorithm “a”的正确术语是什么;“凝聚”;事件队列?

Algorithm “a”的正确术语是什么;“凝聚”;事件队列?,algorithm,data-structures,Algorithm,Data Structures,假设我有一个“事件”对象队列,其中每个对象都有一个“事件类型”和某种数据字段。在C++中,可以是这样的: enum class EventType { A, B, C }; struct Event { EventType type; std::string message; }; 比方说,我想要一个这些事件的队列,并且我希望该队列具有以下属性: 将事件推送到队列时,将删除所有具有相同EventType的现有事件。因此,当从队列中弹出事件时,它始

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

enum class EventType
{
    A,
    B,
    C
};

struct Event
{
     EventType type;
     std::string message;
};
比方说,我想要一个这些事件的队列,并且我希望该队列具有以下属性:

  • 将事件推送到队列时,将删除所有具有相同
    EventType
    的现有事件。因此,当从队列中弹出事件时,它始终是其
    EventType
  • 不同
    EventType
    的事件按推送顺序弹出。(
    pop()
    不需要任何参数)
因此,队列对于
事件
为先进先出,对于
事件类型
为后进先出

示例:

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

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

C3 B2 A3
这种类型的数据结构有名字吗?有实现的例子吗?


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


结构设计 这个想法很简单

保留表示队列的基础数据结构

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

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


一个简单实现的示例
#包括


考虑和分析
  • 在我的示例中,我使用了
    std::list
    作为底层数据结构。事实上,一个
    std::list
    在频繁删除“中间”时非常方便。此外,删除后,迭代器不会失效。这是一个很好的属性,以防我们想要改进队列

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

  • QueueWithAGoodName::pop
    QueueWithAGoodName::front
    O(1)
进一步考虑
  • 对于这种类型的数据结构,有一个重要的特性。我们每种类型有一个实例。这意味着
    N
    (队列大小)的上限是
    EventType
    s的数量
  • 如果事件类型的数量有限,则可以考虑几个因素,因为(对于常见体系结构)运行时复杂性
    O(N)
    可以近似为
    O(1)
    。在这种情况下,
    std::vector
    作为底层数据结构可能由于缓存而更加有效
  • 在我报告的简单示例中,
    push
    O(N)
    。如果您有几种类型的
    EventType
    s,则可以使数据结构在推送操作中更有效。简而言之,您可以使用辅助
    std::无序_映射(即哈希映射)来避免线性搜索。删除后,
    std::list
    不会使迭代器无效,因此哈希表保持一致

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


结构设计 这个想法很简单

保留表示队列的基础数据结构

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

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


一个简单实现的示例
#包括


考虑和分析
  • 在我的示例中,我使用了
    std::list
    作为底层数据结构。事实上,一个
    std::list
    在频繁删除“中间”时非常方便。此外,删除后,迭代器不会失效。这是一个很好的属性,以防我们想要改进队列

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

  • QueueWithAGoodName::pop
    QueueWithAGoodName::front
    O(1)
进一步考虑
  • 对于这种类型的数据结构,有一个重要的特性。我们每种类型有一个实例。这意味着
    N
    (队列大小)的上限是
    EventType
    s的数量
  • 如果事件类型的数量有限,则可以考虑几个因素,因为(对于常见体系结构)运行时复杂性
    O(N)
    可以近似为
    O(1)
    。在这种情况下,
    std::vector
    作为底层数据结构可能由于缓存而更加有效
  • 在我报告的简单示例中,
    push
    O(N)
    。如果您有几种类型的
    EventType
    s,则可以使数据结构在推送操作中更有效。简而言之,您可以使用一个辅助
    std::unordered\u映射
    
    C3 B2 A3
    
    #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_;
    };