访谈:系统/ API设计

时间:2012-12-27 21:13:42

标签: system-design

这个问题是在一家大型软件公司中提出的。我想出了一个简单的解决方案,我想知道别人对解决方案的看法。

  

您应该为可以的系统设计API和后端   为生活在城市中的人们分配电话号码。电话号码会   从111-111-1111开始到999-999-9999结束。 API应该启用   客户(市内人)要做以下事情:

     
      
  1. 当客户请求电话号码时,会向其分配一个可用号码。
  2.   
  3. 有些客户可能需要花哨的数字,所以他们可以专门要求分配一个数字。如果请求的号码是   然后系统将它分配给它们,否则系统   分配任何可用的号码。
  4.         

    系统无需知道哪个号码分配给哪个号码   客户。同一客户端可以连续发出请求并获得多个请求   他自己的电话号码,但系统没有打扰。在任何   时间点,系统只知道分配了哪些电话号码   以及哪些电话号码是免费的。

111-111-1111至999-999-9999的数字大致相当于80亿个数字。假设内存不是约束,我可以考虑以下两种方法(几乎相似)。

  1. 维护一个长度为80亿的巨大布尔数组,并有一个指向数组索引的next指针(next初始化为零)。如果next指向的值不是免费的,则转发next直到找到空闲号码。当请求花哨的数字时,只需检查相应的索引位置是否空闲并返回该数字。这种方法的缺点是,当以常规方式分配数字时,如果中间有一个巨大的块(比如10亿个)由花式分配分配,那么next指针必须移动1十亿次。

  2. 为了克服previos设计中提到的困难,我们可以使用某种链接的hashmap。我们维护一个双向链表(这取代了前一个设计中的数组)和另一个与列表长度相同的数组,其中数组的每个元素指向列表中的相应元素。因此,当在常规方法中分配数字时,我们在链表中推进指针并在分配时标记节点(与前一个方法相同)。在分配花哨数字时,我们可以直接在列表中找到与首先索引到数组所需的特殊数字相对应的节点,然后是指针。一旦识别出节点,就会使前一个节点和下一个节点短路,这样我们就不必在进行常规分配时逐个跳过使用过的数字(这是前一种方法的问题)。

    < / LI>

    让我知道我是否走在正确的轨道上。请告诉我任何我遗漏的重要细节。

3 个答案:

答案 0 :(得分:6)

你可以在这个问题的答案中做得更好。

首先,您应该为您设计API。 Icarus3推荐的那个非常好:

string acquireNextAvailableNumber();
boolean acquireRequestedNumber(string special);

第二个返回true(并保留数字),如果可用,否则返回false。

问题没有说明您如何分配电话号码,因此请根据自己的需要进行分配。使第一个“下一个可用”请求返回“111-111-1111”,下一个“111-111-1112”等。这意味着您可以通过记住最后一个分配来记录通过“下一个”分配的所有号码。 (你需要问'111-111-1119'之后是否是“111-111-1120”或111-111-1121“,但这是你应该问的那种东西。无论如何,无论如何,重要的是你可以知道下一个数字是什么,知道最后分配的数字。)

您需要单独存储的特殊要求。哈希表工作,但BST或简单的有序列表也是如此。这取决于你想要的空间和速度之间的权衡,以及可能要求特殊数字的频率。我会在其余部分使用BST(按编号排序),原因我会来。

那么,你如何编码呢?对于下一个分配的号码:

  1. 查看最后分配的号码,然后按顺序查找下一个号码。
  2. 检查该号码是否未分配为特殊号码。您可以使用BST快速完成此操作,因为如果它在那里,它将是BST中的最低条目。
  3. 如果该号码在“特殊号码”数据库中,请递增“已分配号码”值(包括该号码)并从特殊号码中删除该条目。然后重复此过程,直到得到一个不在特殊数字中的数字。
  4. 请注意,此过程可确保低于“next”分配的最后一个“特殊数字”不会出现在特殊数字数据库中。随着“最后分配的正常数量”的增加,它会吸收任何小于该值的特殊数字,将其从表中删除。这可以确保当我们询问序列中的下一个数字是否在特殊数字数据库中时,我们只需要查看最低的条目。

    检查特殊号码很容易。如果它低于分配的最后一个“正常”号码,则它不可用。否则,您要检查它是否存在于BST中。如果没有,则将其添加到BST。

    您可以通过在BST中存储不仅仅是单个数字,而是存储数字范围来优化此过程。如果分配的特殊数字是密集的,那么它会减少树中的空间量以及在那里找到的访问次数。在测试过程中,如果'下一个'数字发现大小为n的数量,那么你可以立即将最高正常数增加n,而不必绕圈数n次。

答案 1 :(得分:0)

首先,您没有对API进行原型设计。例如,如果我必须设计这些API,我将发布2个API。

string acquireNextAvailableNumber();
string acquireRequestedNumber(string special);

其次,您需要决定如何实施它。代码驱动或数据驱动?

您可以维护所有这些数字的哈希值(它会消耗内存)并快速查询数字的可用性。或

您可以维护单个列表以仅存储分布式数字(较少的内存)。因此,每当请求到来时,您就开始在该列表中搜索1到n个数字(增加时间复杂度)。如果没有任何第一个(或请求的)号码,则将其分配给客户端并在列表中添加该条目。

因为有数十亿的数字,你需要考虑空间和时间之间的权衡。

您还可以利用数据库。

答案 2 :(得分:0)

为增强先前的答案,任何BST可能都不够好,因为插入或删除会使其不平衡。平衡的BST,例如红黑树,应该是一个不错的选择。 因此,可以创建一棵红黑树并在开头填充以表示可用数字,并且每次分配都应从其中删除一个元素。

  • init(from, to)-可以在O(n)时间内完成,一个简单的实现就是O(n log n)。但这是服务器启动时的一次性初始化
  • acquireNextAvailableNumber()-应该删除最小的元素,时间成本O(1)
  • acquireRequestedNumber(special)-应该进行搜索并删除找到的元素,保证的时间成本为O(log n)

在Java中,可以使用TreeSet<String>TreeSet<Integer>,因为它是通过Red-Black Tree实现的。

下一个问题可能是几个请求处理线程将访问您的API,因此,由于Java的TreeSet不是线程安全的,因此您应该在初始化时将其包装为:

TreeSet numbers = init(...);
SortedSet availableNumbers = Collections.synchronizedSortedSet(numbers);