LeetCode 第 262 题全解析:从 SQL 到 Swift 的数据分析实战

在这里插入图片描述
在这里插入图片描述

摘要

在实际业务中,打车平台要监控行程的取消率,及时识别服务质量的问题。这篇文章围绕 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 1
               ELSE 0
            END) * 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 表,我们会:

  1. 解析数据结构

  2. 过滤掉被封禁的用户

  3. 筛选出符合时间条件的行程

  4. 分天统计总行程数和取消的行程数

  5. 最终计算每天的取消率并打印结果

代码示例(可运行 Demo)

import Foundation

struct Trip {
    let id: Int
    let clientId: Int
    let driverId: Int
    let status: String
    let requestAt: String
}

struct User {
    let userId: Int
    let banned: String
    let 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.requestAt
    var entry = result[date] ?? (0, 0)
    if trip.status == "cancelled_by_driver" || trip.status == "cancelled_by_client" {
        entry.cancelled += 1
    }
    entry.total += 1
    result[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 数据源,构建一个完整的监控看板。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

网罗开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值