【问题标题】:Rank elements based on their frequency in logarithmic time complexity根据对数时间复杂度中的频率对元素进行排名
【发布时间】:2021-08-11 00:00:56
【问题描述】:

干杯,我正在尝试找到一种算法/数据结构,我可以使用它来按频率对元素进行排名。

例如,假设我有 5 个名字,我想根据它们的频率对它们进行排名。我被连续命名,并且我执行的每个插入和查询都必须在 O(log(n)) 时间内完成,其中 n 是给定名称的数量。

例如,假设我得到了:

  1. “富”
  2. “酒吧”
  3. “酒吧”
  4. “流行”
  5. “富”
  6. “酒吧”

那么,排名第一的应该是“bar”(3次),第二=>“foo”和第三个“pop”。请记住,当两个或多个元素具有相同的频率(和相同的排名)时,我返回的都是正确的。

我尝试使用 Map (Hash) 来保持给出字符串的频率,例如,如果给定 "foo" 我可以返回 3(但不是排名),甚至考虑使用 Set (使用 AVL 树)以按频率排列它们,但我再次无法在对数时间内将其转换为 Ranking 数据结构。有什么想法吗?

【问题讨论】:

  • 无论我使用哪种数据结构,我插入到数据结构中的任何内容都必须是 O(logn)。这会自动拒绝许多选项。链表选项不是我可以使用的,因为例如给定键 = 2(所以我应该返回频率 = 2 的字符串),我必须从频率为 1 的节点传递,所以查询变为O(n)。
  • 假设您指的是我的(现已删除的评论),我最初阅读 O(n)
  • 您可以维护两棵 avl 树,一棵按名称排序,一棵按频率排序。将新名称插入“名称”树时,也将新节点插入“频率树”。频率节点保存频率。如果要插入的名称已经存在,则增加其在频率树中的频率并更新频率树。名称和频率节点都必须存储对彼此节点的引用。
  • 实际上,仔细想想,您只需维护一个带有名称的 avl 树。节点存储名称和频率。此外,您在某处存储对具有最大频率的节点的引用(或者可能只是名称)。对于每次插入,您检查名称是否已经存在,如果存在,您只需增加其频率。对于每次插入,检查频率是否大于“最大频率节点”的频率并相应更新。
  • 是的,它没有,我的错。忘了你需要得到实际排名....已经晚了..

标签: c algorithm data-structures


【解决方案1】:

按名称返回评分。

您可以在恒定时间 O(1) 内进行插入和查询。为此,您需要使用两个结构 hash-map 和我称之为 doubly-linked-list 的结构。

哈希映射包含对 - 名称和指向具有此名称统计信息的列表项/桶的指针。

双向链表存储桶存储两个数字:一个整数表示指向较低存储桶的名称数量 (Rating) 以及其中名称的重复次数 (RepCount)。

初始化:

创建第一个桶,将所有名称放入哈希映射中,并用第一个桶的地址初始化指针。使用RepCount = INFINITYRating = #names 创建另一个存储桶。

操作:

插入名称。 找到对应的桶Target的地址,检查OneMore.RepCount == Target.RepCount + 1为真的桶OneMore是否存在。如果存在则--OneMore.Rating,如果不存在则使用RepCount = Target.RepCount + 1Rating = NextToTarget.Rating - 1 创建一个。注意 NextToTarget 由于初始化而始终存在。将哈希映射条目重新指向OneMore

查询评分。从哈希映射中提取适当的指针并读取Target.Rating

按评级返回名称(和按名称评级)

您需要两个哈希映射和双向链表。在哈希映射names 中存储name => name-in-list*,在哈希映射ratings 中存储评级和指向列表rating => (first, last) 中具有此评级的名字和姓氏的指针。在列表中存储对 (name, rating) 的顺序如下所述。

初始化:

将所有名称插入列表。将单个条目插入哈希映射 (0, (list.head, list.tail))

操作:

插入名称。 使用 names 恢复名称列表 node。使用ratings 找到node.rating 完成并将node 移到它旁边,将其评级提高一。将新评级与下一个节点的评级进行比较,看看是否需要更新现有评级或在 ratings 中创建新评级。如果旧评级现在为空,请删除 ratings 条目,如果 nodefirstlast,则更新它。

查询名称。使用ratings[..].first,如果不存在则返回null

查询评分。返回names[..].rating

【讨论】:

  • 据我了解,在哈希图中,您在名称(字符串)和链接列表中的“桶”之间建立了联系。但是,如果查询评级,要从哈希映射中提取适当的指针,您必须插入字符串名称,对吗?但我希望查询基于“评级”,这就是我得到的。我是不是误会了什么?
  • @george.zrs 抱歉,为此,我更新了答案。
  • 如果你用一个例子解释了它是如何工作的,那将会很有帮助。例如,我首先使用 0 => "(first/last) 所以 NULL 初始化评级映射。我得到“foo”,我将它放入列表并将“foo”映射到它的节点位置(指针)。我检索位置。现在我查询评级图,我检索到第 0 个评级的最后一个节点它为 NULL,所以“第一”。你说“将节点移动到它旁边”是什么意思?连接两个节点他们指向彼此的下一个/上一个?如果我理解正确,那么这又是由于 O(n) 插入列表而被禁止的东西。
  • @george.zrs 在列表中找到一些东西是 O(n) 但插入是 O(1)。就像你现在last 一样,你可以在 O(1) 中插入 next 。我今天会尝试用图片更新答案。
  • 非常感谢您的宝贵时间。我真的很感激 =) =) =)。是的,我上面的错误,我想到了一个单链表,我们只能在最后插入。对不起,愚蠢的评论。
最近更新 更多