LeetCode--571. 给定数字的频率查询中位数

129 阅读3分钟

1 题目描述

Numbers 表:

+-------------+------+  
| Column Name | Type |  
+-------------+------+  
| num         | int  |  
| frequency   | int  |  
+-------------+------+  

num 是这张表的主键 (具有唯一值的列)
这张表的每一行表示某个数字在该数据库中的出现频率
中位数是将数据样本中半数较高值和半数较低值分隔开的值
编写解决方案, 解压 Numbers 表, 报告数据库中所有数字的中位数. 结果四舍五入至一位小数

2 测试用例

输入:
Numbers 表:

+-----+-----------+  
| num | frequency |  
+-----+-----------+  
| 0   | 7         |  
| 1   | 1         |  
| 2   | 3         |  
| 3   | 1         |  
+-----+-----------+  

输出:

+--------+  
| median |  
+--------+  
| 0.0    |  
+--------+  

解释:
如果解压这个 Numbers 表, 可以得到 [0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 3], 所以中位数是 (0 + 0) / 2 = 0

3 解题思路

位置 1位置 2位置 3位置 4位置 5位置 6位置 7位置 8位置 9位置 10位置 11位置 12
000000012223

总数为偶数场景: 中位数的位置有两个, 假设总数为 n, 计算中位数位置的公式为 n/2n/2 + 1, 上面的数据总数为 12, 此测试用例的中位数计算出来是 6, 7, 使用升序和降序分别统计各个数字最后出现的位置, 交叉锁定中位数的位置区间

位置 1位置 2位置 3位置 4位置 5位置 6位置 7位置 8位置 9位置 10位置 11
00000012223

总数为奇数场景: 中位数的位置是一个, 假设总数为 n, 计算中位数位置的公式为 (n+1)/2, 上面的数据总数为 11, 此测试用例的中位数计算出来是 6, 使用升序和降序分别统计各个数字最后出现的位置, 交叉锁定中位数的位置区间

  1. 按照升序排序依次累积统计数字的总数, 按照倒序排序依次累计统计数字的总数, 所有数字的总数据量
select num,  
     sum(frequency) over (order by num desc) desc_frequency,  
     sum(frequency) over (order by num asc)  asc_frequency,  
     sum(frequency) over ()                  total_frequency  
from Numbers  

查询结果

+---+--------------+-------------+---------------+  
|num|desc_frequency|asc_frequency|total_frequency|  
+---+--------------+-------------+---------------+  
|0  |12            |7            |12             |  
|1  |5             |8            |12             |  
|2  |4             |11           |12             |  
|3  |1             |12           |12             |  
+---+--------------+-------------+---------------+  
  1. 通过 desc_frequency >= total_frequency / 2 and asc_frequency >= total_frequency / 2, 通过升序统计和降序统计的交集来锁定中位数的位置
select *  
from (select num,  
             sum(frequency) over (order by num desc) desc_frequency,  
             sum(frequency) over (order by num asc)  asc_frequency,  
             sum(frequency) over ()                  total_frequency  
      from Numbers) as temp  
where desc_frequency >= total_frequency / 2  
  and asc_frequency >= total_frequency / 2;  

查询结果

+---+--------------+-------------+---------------+  
|num|desc_frequency|asc_frequency|total_frequency|  
+---+--------------+-------------+---------------+  
|0  |12            |7            |12             |  
+---+--------------+-------------+---------------+  

测试用例的数据, 总共有 12 个数字, 中位数的位置是 6, 7, 而这两个位置的数字是 0
如果测试数据换成下面

+---+---------+  
|num|frequency|  
+---+---------+  
|0  |6        |  
|1  |2        |  
|2  |3        |  
|3  |1        |  
+---+---------+  

查询结果

+---+--------------+-------------+---------------+  
|num|desc_frequency|asc_frequency|total_frequency|  
+---+--------------+-------------+---------------+  
|0  |12            |6            |12             |  
|1  |6             |8            |12             |  
+---+--------------+-------------+---------------+  

总数据的量为 12, 中位数的位置为 6,7 这两个位置的数字分别是 0 和 1
3. 计算中位数位置的数字的平均值, 结果四舍五入保留一位小数

select round(avg(num), 1) as median  
from (select num,  
             sum(frequency) over (order by num desc) desc_frequency,  
             sum(frequency) over (order by num asc)  asc_frequency,  
             sum(frequency) over ()                  total_frequency  
      from Numbers) as temp  
where desc_frequency >= total_frequency / 2  
  and asc_frequency >= total_frequency / 2;  

两组测试数据的结果分别是

+------+  
|median|  
+------+  
|0.0   |  
+------+  
  
+------+  
|median|  
+------+  
|0.5   |  
+------+