LeetCode 379 - 电话目录管理系统(Design Phone Directory)
文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 代码解析
- 示例测试及结果
- 输出结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
在日常软件开发中,我们常常需要设计类似“资源池”的功能,比如电话号码分配、账号生成、甚至是座位预定。资源池的关键点就是:要能快速分配、检查和回收。LeetCode 第 379 题“电话目录管理系统”就是一个很典型的资源池设计问题,它要求我们在有限的号码池中进行高效的分配和管理。
这道题不仅考察了数据结构的应用,还非常贴近实际场景。本文会带你从需求出发,一步步拆解实现,并且写出可运行的 Swift Demo。
描述
题目描述如下:
设计一个电话目录管理系统,支持以下 3 个操作:
- get(): 提供一个未分配的号码,如果没有可用号码则返回 -1。
- check(number): 检查某个号码是否未被分配。
- release(number): 释放某个号码,将它重新放回号码池。
初始化时,系统会指定一个上限 maxNumbers
,即最多可以存放多少个号码,号码范围是从 0
到 maxNumbers - 1
。
例如:
PhoneDirectory directory = new PhoneDirectory(3);
directory.get(); // 返回 0
directory.get(); // 返回 1
directory.check(2); // 返回 true
directory.get(); // 返回 2
directory.check(2); // 返回 false
directory.release(2); // 释放号码 2
directory.check(2); // 返回 true
题解答案
我们要实现一个支持 分配、检查、释放 的号码池,关键有几点:
- 快速获取一个未分配的号码。
- 快速判断某个号码是否已分配。
- 快速释放号码,并且保证后续可以再次分配。
常见的解法有两种:
- 用队列(或集合)保存所有可用的号码,每次
get()
从队列里取一个。 - 用一个布尔数组(或哈希集合)来标记某个号码是否被使用。
我们可以结合这两种思路:
- 用 队列 存储未分配的号码。
- 用 Set 记录当前已分配的号码,方便
check
操作。
这样可以在 O(1) 的时间内完成所有操作。
题解代码分析
下面是 Swift 的完整实现:
import Foundationclass PhoneDirectory {private var available: [Int] // 队列,存储未分配的号码private var used: Set<Int> // 已分配的号码集合init(_ maxNumbers: Int) {self.available = Array(0..<maxNumbers)self.used = Set<Int>()}// 获取一个未分配的号码func get() -> Int {if available.isEmpty {return -1}let number = available.removeFirst()used.insert(number)return number}// 检查号码是否可用func check(_ number: Int) -> Bool {return !used.contains(number)}// 释放号码func release(_ number: Int) {if used.contains(number) {used.remove(number)available.append(number)}}
}
代码解析
-
初始化
available
队列存储从0
到maxNumbers - 1
的所有号码。used
集合存储已经分配出去的号码。
-
get()
- 如果
available
队列为空,说明没有号码可用了,返回 -1。 - 否则取出队首号码,并把它放入
used
集合。
- 如果
-
check(number)
- 如果号码不在
used
集合里,就说明可用。
- 如果号码不在
-
release(number)
- 如果号码在
used
里,就从used
移除,并放回available
队列。
- 如果号码在
示例测试及结果
我们来写一个小 Demo 测试一下:
let directory = PhoneDirectory(3)print(directory.get()) // 输出 0
print(directory.get()) // 输出 1
print(directory.check(2)) // 输出 true
print(directory.get()) // 输出 2
print(directory.check(2)) // 输出 false
directory.release(2)
print(directory.check(2)) // 输出 true
输出结果
0
1
true
2
false
true
和题目要求完全一致。
时间复杂度
get()
操作:O(1),因为队列取数和集合插入都是常数时间。check()
操作:O(1),哈希集合查询。release()
操作:O(1),集合删除和队列添加。
因此整体是 O(1),非常高效。
空间复杂度
available
队列存储最多maxNumbers
个号码。used
集合同样最多存储maxNumbers
个号码。
因此空间复杂度是 O(maxNumbers)。
总结
这道题本质上就是一个 号码池(资源池)管理系统,关键点是保证操作的高效性。我们用队列存储可用号码,用集合存储已用号码,这样每个操作都能在 O(1) 时间内完成。
在实际场景中,这个思路同样适用:
- 数据库连接池:管理空闲和使用中的连接。
- 账号/工号分配系统:快速生成和回收账号。
- IP 地址池管理:分配和释放 IP 地址。
所以这道题不仅是个数据结构练习,更是很多工程实践中的缩影。