这道LeetCode 379题要求我们设计一个电话目录管理系统,核心功能是高效管理一组号码资源。系统需要支持三个基本操作:分配号码、检查号码可用性和释放号码。这类问题在实际工程中非常常见,比如数据库连接池管理、线程池资源分配等场景。
作为面试题,它考察的是我们对数据结构的理解与选择能力。我们需要在保证功能正确的前提下,尽可能优化操作的时间复杂度。题目给出的约束条件(maxNumbers ≤ 10^4,总操作次数约2×10^4)意味着我们需要设计出O(1)时间复杂度的解决方案。
要实现高效的号码管理,我们需要考虑以下几种数据结构的特性:
数组(Array):
集合(Set):
队列(Queue):
链表(Linked List):
经过分析,我们采用Set + Array的组合方案:
swift复制class PhoneDirectory {
private let maxNumbers: Int
private var available: Set<Int>
private var queue: [Int]
// 其余实现...
}
这种设计的精妙之处在于:
available Set负责快速查找(O(1))queue Array负责快速分配(O(1) removeLast)swift复制init(_ maxNumbers: Int) {
self.maxNumbers = maxNumbers
self.available = Set(0..<maxNumbers)
self.queue = Array(0..<maxNumbers)
}
初始化时我们需要:
时间复杂度:O(n),因为需要初始化n个元素
swift复制func get() -> Int {
guard !queue.isEmpty else {
return -1
}
let num = queue.removeLast()
available.remove(num)
return num
}
关键点:
注意:使用removeLast()而非removeFirst()是为了保证O(1)时间复杂度,因为Swift数组头部操作是O(n)
swift复制func check(_ number: Int) -> Bool {
guard number >= 0 && number < maxNumbers else {
return false
}
return available.contains(number)
}
实现要点:
swift复制func release(_ number: Int) {
guard number >= 0 && number < maxNumbers else {
return
}
if !available.contains(number) {
available.insert(number)
queue.append(number)
}
}
关键逻辑:
技巧:使用contains检查避免重复释放,保证幂等性
| 操作 | 仅用Set | 仅用Array | Set+Array |
|---|---|---|---|
| get() | O(n)查找 | O(1)尾部移除 | O(1) |
| check() | O(1) | O(n)查找 | O(1) |
| release() | O(1) | O(n)查找+插入 | O(1) |
虽然使用了两个数据结构存储号码,但:
这是典型的"空间换时间"策略,在资源管理系统中很常见。
swift复制// check方法中的边界检查
guard number >= 0 && number < maxNumbers else {
return false
}
// release方法中的边界检查
guard number >= 0 && number < maxNumbers else {
return
}
swift复制if !available.contains(number) {
available.insert(number)
queue.append(number)
}
这段代码确保:
swift复制guard !queue.isEmpty else {
return -1
}
当所有号码都已分配时,get()返回-1表示失败,符合题目要求。
swift复制class ConnectionPool {
private var available: Set<Connection>
private var queue: [Connection]
func getConnection() -> Connection? {
guard !queue.isEmpty else { return nil }
let conn = queue.removeLast()
available.remove(conn)
return conn
}
func release(_ conn: Connection) {
if !available.contains(conn) {
available.insert(conn)
queue.append(conn)
}
}
}
swift复制class ThreadPool {
private var availableThreads: Set<WorkerThread>
private var idleThreads: [WorkerThread]
func getThread() -> WorkerThread? {
guard !idleThreads.isEmpty else { return nil }
let thread = idleThreads.removeLast()
availableThreads.remove(thread)
return thread
}
func release(_ thread: WorkerThread) {
if !availableThreads.contains(thread) {
availableThreads.insert(thread)
idleThreads.append(thread)
}
}
}
swift复制class MeetingRoomManager {
private var availableRooms: Set<Int>
private var freeRooms: [Int]
func bookRoom() -> Int? {
guard !freeRooms.isEmpty else { return nil }
let room = freeRooms.removeLast()
availableRooms.remove(room)
return room
}
func releaseRoom(_ room: Int) {
if !availableRooms.contains(room) {
availableRooms.insert(room)
freeRooms.append(room)
}
}
}
swift复制func testPhoneDirectory() {
let dir = PhoneDirectory(3)
// 测试初始状态
XCTAssertEqual(dir.check(0), true)
XCTAssertEqual(dir.check(1), true)
XCTAssertEqual(dir.check(2), true)
// 测试分配
let num1 = dir.get()
XCTAssertTrue([0,1,2].contains(num1))
XCTAssertEqual(dir.check(num1), false)
// 测试释放
dir.release(num1)
XCTAssertEqual(dir.check(num1), true)
// 测试边界
XCTAssertEqual(dir.check(-1), false)
XCTAssertEqual(dir.check(3), false)
}
swift复制func testPerformance() {
let dir = PhoneDirectory(10_000)
measure {
// 执行大量操作
for _ in 0..<20_000 {
let num = dir.get()
if num != -1 {
dir.release(num)
}
}
}
}
预期结果:即使在最大数据量下,所有操作也应保持O(1)时间复杂度。
swift复制class PhoneDirectoryQueueOnly {
private var queue: [Int]
func get() -> Int {
return queue.isEmpty ? -1 : queue.removeFirst()
}
func check(_ number: Int) -> Bool {
return queue.contains(number)
}
func release(_ number: Int) {
if !queue.contains(number) {
queue.append(number)
}
}
}
缺点:
swift复制class PhoneDirectoryBitVector {
private var bitVector: [Bool]
private var nextAvailable: Int
init(_ maxNumbers: Int) {
bitVector = Array(repeating: true, count: maxNumbers)
nextAvailable = 0
}
func get() -> Int {
guard nextAvailable < bitVector.count else { return -1 }
let num = nextAvailable
bitVector[num] = false
nextAvailable = bitVector.firstIndex(of: true) ?? bitVector.count
return num
}
func check(_ number: Int) -> Bool {
guard number >= 0 && number < bitVector.count else { return false }
return bitVector[number]
}
func release(_ number: Int) {
guard number >= 0 && number < bitVector.count else { return }
if !bitVector[number] {
bitVector[number] = true
if number < nextAvailable {
nextAvailable = number
}
}
}
}
优点:
缺点:
Swift作为现代编程语言:
实际生产环境中需要考虑线程安全:
swift复制class ConcurrentPhoneDirectory {
private let lock = NSLock()
private var available: Set<Int>
private var queue: [Int]
func get() -> Int {
lock.lock()
defer { lock.unlock() }
guard !queue.isEmpty else { return -1 }
let num = queue.removeLast()
available.remove(num)
return num
}
// 其他方法也需要加锁...
}
可以根据需求扩展:
在实现这类资源管理系统时,我通常会:
这种设计模式的价值在于它的通用性。掌握了这种Set+Array的组合方案后,你可以轻松应对各种资源管理场景。在实际工程中,我曾在数据库连接池、线程池、缓存对象管理等多个场景成功应用过这种模式。