名称和伪ID的数据结构:使用哈希表还是BST?

时间:2016-12-16 14:34:17

标签: c++ data-structures time-complexity

我的任务是构建一个数据结构,存储从整数“伪ID”到名称的映射。我可以在表中插入新名称,其中每个名称与许多伪ID相关联,前提是没有任何伪ID。我需要支持按ID查找和按ID删除,如果删除任何人的伪ID,它将删除该人的所有伪ID。

这个程序运行在一个看起来像这样的脚本:

I JackSmart 3 9 1009 1000009
L 1000009
I TedPumpkinhead 1 19
I PeterMeter 1 9
L 19
D 19
L 19
I JohnCritic 2 1 19
L 19
L 1
L 9

这里,每行的第一个字符决定了如何解释它。

  • 以I开头的行是插入。该行的其余部分将包含一个名称,后跟该名称的许多伪ID,然后是每个伪ID。除非已使用任何伪ID,否则应插入该名称。

  • 以L开头的行是查找。该行包含要查找的伪ID。我需要打印与之关联的名称,或者报告不存在这样的名称。

  • 以D开头的行是删除。该行包含要删除的伪ID。然后,我需要从表中删除与该伪ID相关联的人员,以便通过任何他们的伪ID查找它们现在失败。

此任务的输出(根据顶部的示例文件)将是:

ok
JackSmart
ok
no
TedPumpkinhead
ok
no
ok
JohnCritic
JohnCritic
JackSmart

这里最好的方法是哪种?我应该使用哪种数据结构来完成这项任务?由于插入和删除,我认为这是BST。有什么想法吗?

此外,这需要有效运行。每个任务都应该在最坏情况下运行O(log n)。

1 个答案:

答案 0 :(得分:0)

让我们尝试解决这个问题的不同方法的进展。

解决此问题的一个选择是存储某种关联数组,其中每个键都是一个伪ID,每个值都是一个名称。要进行插入,请检查每个伪ID是否都是空闲的。如果是,则在每个插槽中插入一个键/值对。要进行查找,只需按ID查找并返回找到的内容即可。要进行删除,请查找与键/值对关联的键,然后遍历表并删除具有该值的每个键/值对。

这种方法对于插入和查找来说非常快,但删除速度确实很慢。具体来说,插入带有k个伪ID的名称需要O(k)关联数组操作(每个运行时取决于我们如何实现关联数组),查找名称需要O(1)关联数组操作,但删除需要O (n)关联数组运算。

为了加快速度,我们可以使用的一个技巧是,除了实际名称之外,每个值存储一个与该名称关联的所有伪ID的列表。这样,当我们执行删除时,我们可以查找需要删除哪些伪ID,而不必逐字查看associatve数组中的每个条目。这要快得多 - 它将删除的成本降低到O(k)关联数组操作,其中k是删除的元素数。

为了使事情更快,一个聪明的想法是让关联数组中的每个值都是指向存储两条信息的结构的指针:与条目相关联的实际名称,加上一个标志,表明该元素是否已被删除。然后,每个伪ID密钥存储指向该共享结构的指针。每当您执行删除操作时,只需设置"已删除"即可删除键/值对的所有副本。结构上的标志为true。但是,键/值映射仍然存在于表中,因此每当您执行查找或插入并找到存储在某处的键/值对时,在得出键的伪ID已经在使用之前,您就是&#39 ;检查国旗。如果它被标记为已删除,那么您可以假装该ID已不再使用。如果未将其标记为已删除,则表示该条目已生效,应跳过该条目。

这里有一些伪代码:

Maintain an associative array "table."

To do a lookup(pseudoID):
   if table[pseudoID] doesn't exist, return null
   if table[pseudoID] exists, but table[pseudoID].deleted is true, return null
   return table[pseudoID].value

To do insertion(name, id1, id2, ..., idk):
   for (each id):
      if (lookup(id) != null) return false

   create a new structure:
      entry.value   = name
      entry.deleted = false 

   for (each id):
      table[id] = entry

To do delete(id):
   lookup(id) == null: return false;
   table[id].deleted = true

总的来说,这种新方法需要O(k)关联数组操作,用于插入具有k个伪ID的名称,用于查找的O(1)关联数组操作,以及用于删除的O(1)关联数组操作

现在的问题是表示关联数组的最佳结构。请注意,我们永远不必担心按排序顺序访问任何内容,因此虽然我们可以使用BST,但最好使用哈希表来实现此目的,因为它(期望值)要快得多。因此,如果在BST和哈希表之间做出选择,我会使用哈希表,因为它可能会快得多。如果您需要保证最坏情况的效率,可以使用BST。

对于BST,插入在时间O(k log n)中运行k个ID,并且查找和删除都将在最坏情况下的时间O(log n)中运行。对于哈希表,插入在k个ID的预期时间O(k)内运行,并且查找和删除都将在预期的O(1)时间内运行。

另一个选项,取决于可用于伪ID的数字类型,可能是使用二进制trie。这取决于键中可以有多少位以及您期望所有内容的密集程度。