LeetCode第178题_分数排名

LeetCode 第178题:分数排名

题目描述

表: Scores

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| id          | int     |
| score       | decimal |
+-------------+---------+
id 是该表的主键。
该表的每一行都包含了一场比赛的分数。score 是一个有 2 位小数的浮点值。

编写 SQL 查询对分数进行排序。排名按以下规则计算:

  • 分数应按从高到低排列。
  • 如果两个分数相同,那么两个分数的排名应该相同。
  • 在排名相同的分数后,排名数应该是下一个连续的整数。换句话说,排名之间不应该有空缺的数字。

score 降序返回结果表。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

输入: 
Scores 表:
+----+-------+
| id | score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+
输出: 
+-------+------+
| score | rank |
+-------+------+
| 4.00  | 1    |
| 4.00  | 1    |
| 3.85  | 2    |
| 3.65  | 3    |
| 3.65  | 3    |
| 3.50  | 4    |
+-------+------+

提示

  • score 的数值取值在 0 到 100 之间。
  • 每个 score 都是两位精度的浮点数。

解题思路

方法一:使用窗口函数 DENSE_RANK()

MySQL 支持窗口函数,我们可以使用 DENSE_RANK() 函数对分数进行排名。DENSE_RANK() 函数会为相同的值分配相同的排名,并且排名是连续的,没有空缺。

关键点:

  1. 使用 DENSE_RANK() 窗口函数按照分数降序排列
  2. 返回分数和对应的排名
  3. 最后对结果按照分数降序排序

时间复杂度:O(n log n),其中 n 是 Scores 表中的行数(排序的时间复杂度)
空间复杂度:O(n)

方法二:使用子查询和计数(不使用窗口函数)

对于不支持窗口函数的 MySQL 版本,我们可以使用子查询来实现相同的功能。

关键点:

  1. 对于每个分数,统计有多少个不同的分数大于或等于它
  2. 使用子查询和 COUNT(DISTINCT) 实现这个统计
  3. 最后按照分数降序排序结果

时间复杂度:O(n²),其中 n 是 Scores 表中的行数(嵌套循环的时间复杂度)
空间复杂度:O(n)

方法三:使用用户变量(不使用窗口函数)

这种方法使用 MySQL 的用户变量来跟踪当前排名和分数,是一种更高效的不使用窗口函数的解决方案。

关键点:

  1. 按照分数降序排序
  2. 使用用户变量跟踪上一个分数和当前排名
  3. 当分数变化时更新排名

时间复杂度:O(n log n),其中 n 是 Scores 表中的行数(排序的时间复杂度)
空间复杂度:O(n)

代码实现

SQL 实现(方法一:使用窗口函数)

SELECT score, DENSE_RANK() OVER (ORDER BY score DESC) AS 'rank'
FROM Scores
ORDER BY score DESC;

SQL 实现(方法二:使用子查询)

SELECT S1.score, 
       (SELECT COUNT(DISTINCT S2.score) 
        FROM Scores S2 
        WHERE S2.score >= S1.score) AS 'rank'
FROM Scores S1
ORDER BY S1.score DESC;

SQL 实现(方法三:使用用户变量)

SELECT score, 
       @rank := IF(@prev = score, @rank, @rank + 1) AS 'rank', 
       @prev := score
FROM Scores, 
     (SELECT @rank := 0, @prev := -1) AS init
ORDER BY score DESC;

性能分析

各SQL实现的性能对比:

实现方法执行用时内存消耗特点
方法一217 ms0B使用窗口函数,代码最简洁
方法二331 ms0B使用子查询,适用性广但性能较差
方法三238 ms0B使用用户变量,兼顾性能和兼容性

补充说明

代码亮点

  1. 方法一使用 DENSE_RANK() 窗口函数,代码简洁明了,是最推荐的解决方案
  2. 方法二不依赖窗口函数,适用于旧版本的 MySQL,但性能较差
  3. 方法三使用 MySQL 的用户变量,在不支持窗口函数的环境中是一个好的折衷方案

窗口函数说明

  • DENSE_RANK():为每一组值分配一个排名,排名是连续的(没有间隔)。相同的值具有相同的排名。
  • RANK():与 DENSE_RANK() 类似,但会在相同排名后产生间隔。
  • ROW_NUMBER():为每一行分配一个唯一的整数(即使值相同)。

在这道题中,我们需要使用 DENSE_RANK() 而不是 RANK(),因为题目要求"排名之间不应该有空缺的数字"。

常见错误

  1. 使用 RANK() 而不是 DENSE_RANK(),导致排名不连续
  2. 在方法三中没有正确初始化用户变量
  3. 忘记按照分数降序排序最终结果
  4. 在方法二中没有使用 DISTINCT,导致相同分数被多次计数

相关题目

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值