|
| 1 | +# [3124. 查找最长的电话 🔒](https://leetcode.cn/problems/find-longest-calls) |
| 2 | + |
| 3 | +[English Version](/solution/3100-3199/3124.Find%20Longest%20Calls/README_EN.md) |
| 4 | + |
| 5 | +<!-- tags: --> |
| 6 | + |
| 7 | +## 题目描述 |
| 8 | + |
| 9 | +<!-- 这里写题目描述 --> |
| 10 | + |
| 11 | +<p>表:<code>Contacts</code></p> |
| 12 | + |
| 13 | +<pre> |
| 14 | ++-------------+---------+ |
| 15 | +| Column Name | Type | |
| 16 | ++-------------+---------+ |
| 17 | +| id | int | |
| 18 | +| first_name | varchar | |
| 19 | +| last_name | varchar | |
| 20 | ++-------------+---------+ |
| 21 | +id 是这张表的主键(有不同值的列)。 |
| 22 | +id 是 Calls 表的外键(引用列)。 |
| 23 | +这张表的每一行都包含 id,first_name 和 last_name。 |
| 24 | +</pre> |
| 25 | + |
| 26 | +<p>表:<code>Calls</code></p> |
| 27 | + |
| 28 | +<pre> |
| 29 | ++-------------+------+ |
| 30 | +| Column Name | Type | |
| 31 | ++-------------+------+ |
| 32 | +| contact_id | int | |
| 33 | +| type | enum | |
| 34 | +| duration | int | |
| 35 | ++-------------+------+ |
| 36 | +(contact_id, type, duration) 是这张表的主键(有不同值的列)。 |
| 37 | +类型是 ('incoming', 'outgoing') 的 ENUM (category)。 |
| 38 | +这张表的每一行包含有 calls, 包括 contact_id,type 和以秒为单位的 duration 的信息。 |
| 39 | +</pre> |
| 40 | + |
| 41 | +<p>编写一个解决方案来找到 <strong>三个最长的呼入</strong> 和 <strong>呼出</strong> 电话。</p> |
| 42 | + |
| 43 | +<p>返回结果表,以 <code>type</code>,<code>duration</code> 和 <code>first_name</code> <em><strong>降序排序</strong> ,<code>duration</code> 的格式必须为 <strong>HH:MM:SS</strong>。</em></p> |
| 44 | + |
| 45 | +<p>结果格式如下所示。</p> |
| 46 | + |
| 47 | +<p> </p> |
| 48 | + |
| 49 | +<p><strong class="example">示例 1:</strong></p> |
| 50 | + |
| 51 | +<div class="example-block"> |
| 52 | +<p><b>输入:</b></p> |
| 53 | + |
| 54 | +<p>Contacts 表:</p> |
| 55 | + |
| 56 | +<pre class="example-io"> |
| 57 | ++----+------------+-----------+ |
| 58 | +| id | first_name | last_name | |
| 59 | ++----+------------+-----------+ |
| 60 | +| 1 | John | Doe | |
| 61 | +| 2 | Jane | Smith | |
| 62 | +| 3 | Alice | Johnson | |
| 63 | +| 4 | Michael | Brown | |
| 64 | +| 5 | Emily | Davis | |
| 65 | ++----+------------+-----------+ |
| 66 | +</pre> |
| 67 | + |
| 68 | +<p>Calls 表:</p> |
| 69 | + |
| 70 | +<pre class="example-io"> |
| 71 | ++------------+----------+----------+ |
| 72 | +| contact_id | type | duration | |
| 73 | ++------------+----------+----------+ |
| 74 | +| 1 | incoming | 120 | |
| 75 | +| 1 | outgoing | 180 | |
| 76 | +| 2 | incoming | 300 | |
| 77 | +| 2 | outgoing | 240 | |
| 78 | +| 3 | incoming | 150 | |
| 79 | +| 3 | outgoing | 360 | |
| 80 | +| 4 | incoming | 420 | |
| 81 | +| 4 | outgoing | 200 | |
| 82 | +| 5 | incoming | 180 | |
| 83 | +| 5 | outgoing | 280 | |
| 84 | ++------------+----------+----------+ |
| 85 | + </pre> |
| 86 | + |
| 87 | +<p><strong>输出:</strong></p> |
| 88 | + |
| 89 | +<pre class="example-io"> |
| 90 | ++-----------+----------+-------------------+ |
| 91 | +| first_name| type | duration_formatted| |
| 92 | ++-----------+----------+-------------------+ |
| 93 | +| Michael | incoming | 00:07:00 | |
| 94 | +| Jane | incoming | 00:05:00 | |
| 95 | +| Emily | incoming | 00:03:00 | |
| 96 | +| Alice | outgoing | 00:06:00 | |
| 97 | +| Emily | outgoing | 00:04:40 | |
| 98 | +| Jane | outgoing | 00:04:00 | |
| 99 | ++-----------+----------+-------------------+ |
| 100 | + </pre> |
| 101 | + |
| 102 | +<p><strong>解释:</strong></p> |
| 103 | + |
| 104 | +<ul> |
| 105 | + <li>Michael 有一通长达 7 分钟的呼入电话。</li> |
| 106 | + <li>Jane 有一通长达 5 分钟的呼入电话。</li> |
| 107 | + <li>Emily 有一通长达 3 分钟的呼入电话。</li> |
| 108 | + <li>Alice 有一通长达 6 分钟的呼出电话。</li> |
| 109 | + <li>Emily 有一通长达 4 分 40 秒的呼出电话。</li> |
| 110 | + <li>Jane 有一通长达 4 分钟的呼出电话。</li> |
| 111 | +</ul> |
| 112 | + |
| 113 | +<p><b>注意:</b>输出表以 type,duration 和 first_name 降序排序。</p> |
| 114 | +</div> |
| 115 | + |
| 116 | +## 解法 |
| 117 | + |
| 118 | +### 方法一:等值连接 + 窗口函数 |
| 119 | + |
| 120 | +我们可以使用等值连接将两张表连接起来,然后使用窗口函数 `RANK()` 计算每个类型的电话的排名。最后,我们只需要筛选出排名前三的电话即可。 |
| 121 | + |
| 122 | +<!-- tabs:start --> |
| 123 | + |
| 124 | +```sql |
| 125 | +WITH |
| 126 | + T AS ( |
| 127 | + SELECT |
| 128 | + first_name, |
| 129 | + type, |
| 130 | + CONCAT( |
| 131 | + LPAD(duration DIV 3600, 2, '0'), |
| 132 | + ':', |
| 133 | + LPAD((duration MOD 3600) DIV 60, 2, '0'), |
| 134 | + ':', |
| 135 | + LPAD(duration MOD 60, 2, '0') |
| 136 | + ) AS duration_formatted, |
| 137 | + RANK() OVER ( |
| 138 | + PARTITION BY type |
| 139 | + ORDER BY duration DESC |
| 140 | + ) AS rk |
| 141 | + FROM |
| 142 | + Calls AS c1 |
| 143 | + JOIN Contacts AS c2 ON c1.contact_id = c2.id |
| 144 | + ) |
| 145 | +SELECT |
| 146 | + first_name, |
| 147 | + type, |
| 148 | + duration_formatted |
| 149 | +FROM T |
| 150 | +WHERE rk <= 3 |
| 151 | +ORDER BY 2, 3 DESC, 1 DESC; |
| 152 | +``` |
| 153 | + |
| 154 | +```python |
| 155 | +import pandas as pd |
| 156 | + |
| 157 | + |
| 158 | +def find_longest_calls(contacts: pd.DataFrame, calls: pd.DataFrame) -> pd.DataFrame: |
| 159 | + merged_data = calls.merge(contacts, left_on="contact_id", right_on="id") |
| 160 | + merged_data["duration_formatted"] = ( |
| 161 | + merged_data["duration"] // 3600 * 10000 |
| 162 | + + merged_data["duration"] % 3600 // 60 * 100 |
| 163 | + + merged_data["duration"] % 60 |
| 164 | + ).apply(lambda x: "{:02}:{:02}:{:02}".format(x // 10000, x // 100 % 100, x % 100)) |
| 165 | + |
| 166 | + merged_data["rk"] = merged_data.groupby("type")["duration"].rank( |
| 167 | + method="dense", ascending=False |
| 168 | + ) |
| 169 | + |
| 170 | + result = merged_data[merged_data["rk"] <= 3][ |
| 171 | + ["first_name", "type", "duration_formatted"] |
| 172 | + ] |
| 173 | + result = result.sort_values( |
| 174 | + by=["type", "duration_formatted", "first_name"], ascending=[True, False, False] |
| 175 | + ) |
| 176 | + return result |
| 177 | +``` |
| 178 | + |
| 179 | +<!-- tabs:end --> |
| 180 | + |
| 181 | +<!-- end --> |
0 commit comments