【问题标题】:Fastest C++ Container: Unique Values最快的 C++ 容器:唯一值
【发布时间】:2011-06-07 06:47:35
【问题描述】:

我正在编写一个与 MySQL 数据库交互的电子邮件应用程序。我有两个表正在获取我的数据,其中一个包含取消订阅,另一个是标准用户表。到目前为止,我正在创建一个指向电子邮件对象的指针向量,并将所有未订阅的电子邮件存储在其中。然后我有一个标准的 SQL 循环,我在其中检查电子邮件是否不在取消订阅向量中,然后将其添加到全局发送电子邮件向量中。我的问题是,有没有更有效的方法来做到这一点?我必须为我系统中的每封电子邮件搜索取消订阅向量,最多 50K 不同的电子邮件。有更好的搜索结构吗?并且,一个更好的结构来维护一个独特的价值观集合?如果它已经包含它,也许会简单地丢弃它?

【问题讨论】:

  • DVK 和 Daniel Trebbien 是对的:在数据库中执行此操作几乎肯定会更好。当你说这是不可能的时候,我不相信你——请发布架构的相关部分。
  • 为什么在检查用户是否希望接收之前生成电子邮件?你在这里做额外的工作......
  • @Matthieu:我不是在生成电子邮件内容,而是在收集电子邮件地址以进行交叉引用。

标签: c++ algorithm search data-structures vector


【解决方案1】:

如果您的 C++ 标准库实现支持它,请考虑使用 std::unordered_set std::hash_set

您也可以使用std::set,尽管它的开销可能会更高(这取决于为对象生成哈希的成本与多次比较两个对象的成本)。

如果您确实使用像 setunordered_set 这样的基于节点的容器,您还可以获得与从 vector 中删除元素相比,删除元素相对便宜的优势。

【讨论】:

  • 我想你的意思是std::unordered_setstd::tr1::unordered_set
  • 另外,std::hash_set 不是标准的一部分,如果您没有 TR1 或 c++0x,最好使用 boost::unordered_set
  • @Evan:你是对的;我的意思是std::unordered_set。今天早上我没有喝咖啡。大多数标准库实现都以一种或另一种形式提供hash_set
【解决方案2】:

将您的电子邮件地址存储在std::set 或使用std::set_difference()

【讨论】:

  • +1 for set_difference (因为它已经被烘焙了),但我建议使用 3 个(排序的)向量而不是集合,因为遍历它们应该更快(更好的内存位置)。或者,deque 也可以考虑,如果大小很大,并且您没有使用 Dirkumware(及其小存储桶)。
  • @Matthieu:当使用set_difference 时,你当然会使用排序向量。还有什么?
  • 只是确保 :) 基于节点的容器可能会非常缓慢。
【解决方案3】:
  1. 像这样的任务(集合操作)最好留给执行它们的目的——数据库!

    例如类似于:

     SELECT email FROM all_emails_table e WHERE NOT EXISTS (
         SELECT 1 FROM unsubscribed u where e.email=u.email
     )
    
  2. 如果您需要算法,您可以通过检索电子邮件列表和取消订阅列表作为 ORDERED 列表来快速完成此操作。然后,您可以浏览电子邮件列表(已订购),并在执行此操作时沿着取消订阅列表滑动。这个想法是,你在具有“最大”当前“元素的列表中向前移动 1。这个算法是 O(M+N) 而不是 O(M*N),就像你现在的一样

  3. 或者,您可以做一个哈希映射,从未订阅的电子邮件地址映射到 1。然后您在该映射上执行 find() 调用,每次查找正确的哈希实现是 O(1)。 不幸的是,C++ 中没有 Hash Map 标准 - 请参阅 this SO question for existing implementations(有几个想法有 SGI 的 STL hash_map 和 Boost 和/或 TR1 std::tr1::unordered_map)。

    该帖子中的一个 cmets 表示它将被添加到标准中:“考虑到这一点,C++ 标准库技术报告引入了无序关联容器,它使用哈希表实现,现在已经添加C++ 标准工作草案。”

【讨论】:

  • 很遗憾,由于其中一张表之前的布局方式,我的部分应用程序无法执行此操作。
  • @Josh:您会发布架构的相关部分吗?您是否有单独的表格用于未订阅的电子邮件?
  • 为什么不使用LEFT OUTER JOINSELECT `email` FROM `all_emails_table` AS `e` LEFT OUTER JOIN `unsubscribed` AS `u` ON `e`.`email` = `u`.`email` WHERE `u`.`email` IS NULL;
  • @Daniel - 我来自 TSQL 背景,所以通过 NOT EXISTS 编写反连接比使用 ANSI SQL 的 LEFT OUTER JOIN 更自然。但是两者几乎是相同的(尽管从性能的角度来看并不总是如此 - 谷歌的“反连接性能外部存在”或类似的关于各种数据库服务器的几篇优秀文章 - 我知道有一些用于 MySQL 和 MS SQL )。
【解决方案4】:

我认为最好的方法是在 MySQL 中。您可以使用另一列 BIT 列修改您的用户表架构,以表示“已取消订阅”。更好的是:为“删除日期”添加一个DATETIME 列,默认值为NULL

如果使用BIT 列,您的查询会变成这样:

SELECT * FROM `users` WHERE `unsubscribed` <> 0b1;

如果使用DATETIME 列,您的查询会变成这样:

SELECT * FROM `users` WHERE `date_unsubscribed` IS NULL;

【讨论】:

  • 另外,现在您正在取消订阅用户。当前架构取消订阅电子邮件地址,这并不完全相同。如果用户将他们的电子邮件地址更改为未订阅的,那么他们应该停止接收消息吗? OP 的方法说“是”,这说“否”,我猜这更有可能是正确的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-31
  • 2015-04-30
  • 2010-12-08
  • 1970-01-01
  • 2020-08-17
  • 2018-06-14
  • 1970-01-01
相关资源
最近更新 更多