使用O(1)空间生成唯一ID?

时间:2011-11-12 15:20:40

标签: algorithm

我们有一组对象,我们称之为玩家。我们只能通过随机顺序遍历此组,例如没有Players[0]这样的东西。

每位玩家都有一个ID唯一ID < len(Players)。玩家可以添加到组中并移除。当玩家被移除后,它将释放他的ID,如果玩家被添加,它将获得ID

如果我们想要向玩家添加新播放器,我们必须生成新的唯一ID。在O(1)空间中生成此类ID的最快方法是什么?

6 个答案:

答案 0 :(得分:6)

O(n log n)可以通过二分查找。以a = 0和b = n开头。不变量是在区间[a,b)中存在空闲id。重复以下步骤,直到b - a = 1:令m = a + floor((b - a)/ 2),计算[a,m]和[m,b)中的id数。如果[a,m)小于m - a id,则设置b = m。否则,设置a = m。

答案 1 :(得分:1)

我认为您可以使用队列将已释放的ID排入队列。一旦消耗掉尽可能高的ID,就将队列排队以获得免费ID。这将需要O(1)。

int highestIndex = 0;

添加玩家

if (highestIndex < len(Players)-1){
    ID = ++highestIndex();
} 
else if (!queue.isEmpty()){
    ID = queue.dequeue();
} else{
    // max players reached
}

删除玩家

queue.enqueue(ID);

答案 2 :(得分:0)

基于首先提出的具有固定最大玩家数量的问题:

1)从技术上讲,玩家的大小是O(1)。构建一个1000个插槽的漏洞阵列,每个玩家一个,其中TRUE表示“已分配ID”。当玩家死亡时,将其位的ID设置为false。当新玩家到达时,在位阵列中搜索“假”位;将该ID分配给播放器并设置该位。

时间是O(1),也是一个很大的常数。

根据与任意N名球员一起修改的问题:

2)扩展霍尔的想法:保持一个小的固定大小的数组k&lt; &LT; N作为免费ID的缓存。以TMJ描述的方式使用它。 [TMJ删除了他的回答:它实际上说,“保留一堆未使用的ID,弹出一个未使用的ID,推送新死的ID”]如果在需要新ID时缓存为空,请应用Holzer的方案(甚至可以重新填充执行Holzer方案时的小阵列)。 [Sheesh,Holzer也删除了他的答案,它说“按顺序尝试每个ID并搜索集合;如果没有人拥有该ID,请使用它”(O(N ^ 2)]如果玩家的数量或多或少地到达稳定状态,这将非常快,因为统计上固定大小数组中总会有一些值。

你可以将TMJ的想法与Per的想法结合起来,但你不能在Per扫描期间重新填充数组,只能使用死的玩家ID。

答案 3 :(得分:0)

保留一个布尔数组。在此数组上构造二叉树,使得叶子是数组中的初始值,对于项目i,i + 1,父项是它们的逻辑AND(这意味着其中一个是0)。当你想插入从根向下遍历树以找到第一个空槽(当一个孩子为0时继续向左)。这给出了O(log(n))中的第一个空槽。如果取每个sqrt(n)位组并形成AND父级,则可以得到O(log(log(n))。

答案 4 :(得分:0)

您可以将玩家置于(循环)链接列表中。删除播放器会将其从链中删除,并将其插入另一个列表(“免费”列表)。分配玩家将从“免费”列表中删除(随机)一个并将其插入“活动”列表。

更新: 由于阵列是固定的,你可以使用水印分隔分配给自由球员:

  • 最初{watermark = 0}
  • 免费:{swap [this]&lt; - &gt; [水印-1];递减水印; }
  • 分配:{增量水印; yield warermark-1; }

瞧!

答案 5 :(得分:-1)

你的问题是不正确的。答案是:

ID(newPlayer) = 1000

(您没有要求新玩家ID必须小于1000。)

更严重的是,自O(1000) == O(1)以来,你可以创建一个id_seen[1000]数组,标记你目前所见的所有ID,而不是选择一个你没见过的数组。

为了让您的问题变得有趣,您必须仔细制定,例如: “有N个玩家ID s&lt; K。您只能以未知顺序遍历该集合。使用ID < K添加O(1)的新玩家空间。“

一个(低效)答案:选择随机数X < K。遍历集合。如果您看到有ID == X的玩家,请重新启动。如果不这样做,请将其用作新的ID

评估此算法对给定NK的效率是留给读者的练习; - )