【问题标题】:Data structure that quickly get multiple min(max) elements C++快速获取多个最小(最大)元素的数据结构 C++
【发布时间】:2016-08-04 02:19:21
【问题描述】:

我正在尝试将一些元素存储到一个容器中,并希望根据某些标准有效地获取它。

简单来说,假设:

  1. 记录如下:(ID, Age, Wage)
  2. 每次插入和删除都会返回最低年龄的工资总和

点赞

       ID  Age Wage(k)
insert 1   23  95   // returns 23,95
insert 2   21  75   // returns 21,75
insert 3   27  85   // returns 21,75
insert 4   21  65   // returns 21,140
delete ID=2         // returns 21,65

我能想到几种解决方案:

1.向量 (boost::circular_buffer?)

e.g. [(23,1,95), (21,2,75), (27,3,85), (21,4,65)]
Insert: O(1)
Delete (can use a bit to mark deleted) O(n)
GetMinSum: O(n)

2.排序向量

e.g. [(21,2,75), (21,4,65), (23,1,95), (27,3,85)]
Insert: O(n)
Delete (can use a bit to mark deleted) O(n)
GetMinSum: O(1)

3.地图(RB 树)

std::map<age, vector<record>>
[(21)->[(2,75),(1,65)], (23)->[(1,95)], (27)->[(3,85)]]
GetMinSum: O(1)
Insert: O(logN)
Delete: O(n)

有什么建议吗?

【问题讨论】:

  • “有什么建议吗?”为了什么?更好的容器?你能澄清一下吗?
  • 这个问题的任何部分都没有任何意义,包括您声称的算法运行时。
  • 您可以在标准容器周围使用一些包装器,并在插入时更新最小值/最大值。
  • @Rakete1111 关于如何解决这个问题以获得最佳插入/删除/GetMinSum性能的建议
  • 我会使用 #3 的变体,该类具有数据成员 std::map&lt;Age, vector&lt;Record&gt;&gt; 其中 struct Record { int ID; int Age; double Wage; }; 和返回所需信息的适当插入/删除方法。

标签: c++ boost stl


【解决方案1】:

应该可以使用std::map实现O(1)中步骤2的计算部分:

std::map<int, int> sum_of_wages_by_age;
  1. 地图的key是Age

  2. 地图的价值是给定年龄所有工资的总和。

鉴于迭代std::map 的键必须严格按照弱顺序进行迭代,最低的Age 将始终是映射中的第一个键。因此,最低年龄的工资总和将是:

auto p=sum_of_wages_by_age.begin();

return p == sum_of_wages_by_age.end() ? 0: // Edge case, empty map
          p->second;

我的答案只关注跟踪这张地图的内容,以便可以通过这种方式快速返回最小年龄的工资总和。这与您需要存储&lt;Id, Wage, Age&gt; 记录本身的方式完全不同。那是一个单独的问题。由您决定是否要将它们存储在向量或其他东西中。无论你如何存储它们,sum_of_wages_by_age 的唯一目的是能够快速返回正确的号码。手头的主题仅涉及能够以这种方式有效地返还最低年龄的工资总和。

为了使这种方法起作用,上面的地图需要在插入和删除记录时保持更新。这可以按如下方式完成。

添加新的&lt;ID, Age, Wage&gt; 记录。

除了存储这条记录,工资只需要添加到地图中:

sum_of_wages_by_age[age] += wage;

删除现有的 &lt;ID, Age, Wage&gt; 记录

这个有点刺激。

auto p=sum_of_wages_by_age.find(age);

if ( (p->second -= wage) == 0)
    sum_of_wages_by_age.erase(p);

你想要整洁。删除给定年龄的最后一条记录后,您现在希望从该映射中完全删除该年龄的条目。如果你不关心这个,你可以简单地做

sum_of_wages_by_age[age] -= wage;

请注意,由于要删除的记录必须是先前添加的,因此可以保证地图将包含年龄条目。当然,这只有在wage 不能为零或负数时才有效。但这是一个安全的假设。如您所知,有一条法律规定最低工资必须是多少。

警告

以上示例使用ints 表示工资。如果您需要使用floats 或doubles,在重复加减过程中会累积舍入误差。如果这是一个问题,研究如何正确处理舍入错误将是您的家庭作业。

【讨论】:

  • 看起来是个不错的解决方案。除了你的std::map,我还在考虑使用unordered_mapid-&gt;(record) 来存储记录,这样我们就有O(1) 的插入和删除。
  • 您希望如何在 O(1) 中轻松找到 unordered_map 中的最小键?
  • 最低要求已存储在您的std::map 中。也就是说,我需要一张地图和一张 unordered_map。
【解决方案2】:

确实没有符合您要求的标准容器,但编写您自己的适配器类应该不难。

该类可以包含一个用于实际数据的向量、一个指向具有当前最小年龄的结构的指针或引用的向量、当前最小年龄本身作为堆栈,以及一个包含年龄->总和映射的映射。

  • 添加数据:

    添加新结构时,您比较当前最小年龄(年龄堆栈顶部),如果它大于当前最小值,则只需将其添加到数据向量并更新年龄->总和映射中的总和。

    如果年龄等于当前最小年龄,则添加数据并更新年龄->求和图。

    如果新添加的数据的年龄小于当前最小值,则将其添加到数据向量中,将新的年龄压入堆栈,并在年龄->求和图中添加一个新条目。

  • 删除数据:

    对于所有数据,将其从向量中删除,并更新年龄->求和图。

    如果年龄等于当前最小年龄,并且年龄->和映射中的和已达到零,则弹出年龄堆栈。新总和是年龄->总和图中新时代的总和。

通过使用不断更新的地图和一堆最小年龄,您不必重新计算总和,因为它们始终是最新的。

【讨论】:

  • 对于添加数据,如果年龄大于当前最小值,我想还是需要将其添加到最小堆栈中,堆栈这样做不是很方便
  • @Deqing 嗯,没错。应该教我不要在早餐前早早回答问题。 :) 这是真的,如果年龄不在堆栈中,这将是一个问题。我会在白天多考虑一下,看看我是否能想出办法。无论如何,这(以及 Sam 的回答)应该是一个很好的起点。
  • @Deqing 你实际上并不需要堆栈。 std::map 在密钥上排序,在我的算法中是年龄。最低的年龄(最小的键)将始终是映射中的第一对,而最旧的(最高的)将始终是最后一个。因此,只需跳过堆栈并保留地图,它就会跟踪总和和最低年龄。
猜你喜欢
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-31
  • 2017-12-09
  • 2013-06-16
相关资源
最近更新 更多