文章目录
- 摘要
- 描述
- 题解答案(SQL)
- Swift 题解代码分析
- 代码示例(可运行 Demo)
- 示例测试及结果
- 时间复杂度分析
- 空间复杂度分析
- 总结
- 未来展望
摘要
在实际业务中,打车平台要监控行程的取消率,及时识别服务质量的问题。这篇文章围绕 LeetCode 第 262 题,带大家用 Swift 和 SQL 解题,并深入聊聊如何结合用户数据和行程数据去计算每天的取消率,尤其是只计算“非禁止用户”的数据。咱们还会补充一个可运行的 Swift Demo 来模拟分析过程,并讲解这类题目在数据分析、风控和运营决策中的价值。
描述
这个题目其实就是想让我们统计每天的“非被封禁用户”的取消率。用户数据在一张表里,行程数据在另一张表里。我们要结合这两张表筛选出符合条件的数据:
-
时间在 2013-10-01 到 2013-10-03 之间
-
用户不能是被禁用的(
banned = No
) -
最终结果要按照天维度输出,每天的取消率保留两位小数
而且要注意,只有司机和乘客都不是禁止用户的行程才参与计算。
题解答案(SQL)
虽然你要的是 Swift 题解,但这道题核心在于 SQL 联表计算,我们先看 SQL 部分:
SELECT request_at AS Day,ROUND(SUM(CASE WHEN status IN ('cancelled_by_driver', 'cancelled_by_client') THEN 1ELSE 0END) * 1.0 / COUNT(*), 2) AS "Cancellation Rate"
FROM Trips t
JOIN Users c ON t.client_id = c.users_id AND c.banned = 'No'
JOIN Users d ON t.driver_id = d.users_id AND d.banned = 'No'
WHERE request_at BETWEEN '2013-10-01' AND '2013-10-03'
GROUP BY request_at;
Swift 题解代码分析
我们用 Swift 来模拟整个 SQL 的流程。假设我们有两个 JSON 数据源,分别对应 Trips
表和 Users
表,我们会:
-
解析数据结构
-
过滤掉被封禁的用户
-
筛选出符合时间条件的行程
-
分天统计总行程数和取消的行程数
-
最终计算每天的取消率并打印结果
代码示例(可运行 Demo)
import Foundationstruct Trip {let id: Intlet clientId: Intlet driverId: Intlet status: Stringlet requestAt: String
}struct User {let userId: Intlet banned: Stringlet role: String
}let users: [User] = [.init(userId: 1, banned: "No", role: "client"),.init(userId: 2, banned: "Yes", role: "client"),.init(userId: 3, banned: "No", role: "client"),.init(userId: 4, banned: "No", role: "client"),.init(userId: 10, banned: "No", role: "driver"),.init(userId: 11, banned: "No", role: "driver"),.init(userId: 12, banned: "No", role: "driver"),.init(userId: 13, banned: "No", role: "driver")
]let trips: [Trip] = [.init(id: 1, clientId: 1, driverId: 10, status: "completed", requestAt: "2013-10-01"),.init(id: 2, clientId: 2, driverId: 11, status: "cancelled_by_driver", requestAt: "2013-10-01"),.init(id: 3, clientId: 3, driverId: 12, status: "completed", requestAt: "2013-10-01"),.init(id: 4, clientId: 4, driverId: 13, status: "cancelled_by_client", requestAt: "2013-10-01"),.init(id: 5, clientId: 1, driverId: 10, status: "completed", requestAt: "2013-10-02"),.init(id: 6, clientId: 2, driverId: 11, status: "completed", requestAt: "2013-10-02"),.init(id: 7, clientId: 3, driverId: 12, status: "completed", requestAt: "2013-10-02"),.init(id: 8, clientId: 2, driverId: 12, status: "completed", requestAt: "2013-10-03"),.init(id: 9, clientId: 3, driverId: 10, status: "completed", requestAt: "2013-10-03"),.init(id: 10, clientId: 4, driverId: 13, status: "cancelled_by_driver", requestAt: "2013-10-03")
]let bannedUsers = Set(users.filter { $0.banned == "Yes" }.map { $0.userId })var result: [String: (cancelled: Int, total: Int)] = [:]for trip in trips {guard !bannedUsers.contains(trip.clientId),!bannedUsers.contains(trip.driverId),["2013-10-01", "2013-10-02", "2013-10-03"].contains(trip.requestAt)else { continue }let date = trip.requestAtvar entry = result[date] ?? (0, 0)if trip.status == "cancelled_by_driver" || trip.status == "cancelled_by_client" {entry.cancelled += 1}entry.total += 1result[date] = entry
}for (day, entry) in result {let rate = entry.total == 0 ? 0.0 : Double(entry.cancelled) / Double(entry.total)print("\(day): \(String(format: "%.2f", rate))")
}
示例测试及结果
运行上述 Swift 程序,将会输出:
2013-10-01: 0.33
2013-10-02: 0.00
2013-10-03: 0.50
这与题目给出的标准结果一致。
时间复杂度分析
假设行程数为 n
,用户数为 m
。
-
筛选禁止用户集合:
O(m)
-
遍历 trips 数据:
O(n)
-
最终输出:
O(k)
,其中k
是天数,这里固定为 3 天
总体时间复杂度是:O(n + m)
空间复杂度分析
-
存储禁止用户集合:
O(m)
-
结果字典
result
:最多三天,常量空间
所以总空间复杂度是:O(m)
总结
这题表面是 SQL 题,其实是在考察我们对数据的过滤和聚合能力。无论是用 SQL 写联表查询,还是用 Swift 做数据模拟,都离不开一个核心思想——干净准确的数据处理。
这个例子在实际项目中特别常见,比如:
-
运营要知道每天的取消率趋势
-
数据平台需要过滤掉异常数据(比如封禁用户)
-
数据分析师希望看到更精准的业务 KPI
未来展望
这类数据处理可以推广到更多场景,比如:
-
留存率、点击率、完成率的计算
-
实时监控系统中的数据聚合
-
用户行为数据的质量控制
未来我们可以拓展这个 Demo,接入真实数据库或 API 数据源,构建一个完整的监控看板。