力扣1892,页面推荐Ⅱ,为一个社交媒体网站实施一个页面推荐系统。如果页面被user_id
的 至少一个朋友喜欢 ,而 不被 user_id
喜欢 ,你的系统将 推荐 一个页面到user_id
。
目录
题目描述
表: Friendship
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| user1_id | int |
| user2_id | int |
+---------------+---------+
(user1_id,user2_id) 是 Friendship 表的主键(具有唯一值的列的组合)。
该表的每一行表示用户user1_id和user2_id是好友。
表: Likes
+-------------+---------+
| Column Name | Type |
+-------------+---------+
| user_id | int |
| page_id | int |
+-------------+---------+
(user_id,page_id) 是 Likes 表的主键(具有唯一值的列)。
该表的每一行表示user_id喜欢page_id。
您正在为一个社交媒体网站实施一个页面推荐系统。如果页面被user_id
的 至少一个朋友喜欢 ,而 不被 user_id
喜欢 ,你的系统将 推荐 一个页面到user_id
。
编写一个解决方案来查找针对每个用户的所有可能的 页面建议。每个建议应该在结果表中显示为一行,包含以下列:
user_id
: 系统向其提出建议的用户的ID。page_id
: 推荐为user_id
的页面ID。.friends_likes
:user_id
对应page_id
的好友数。
以 任意顺序返回结果表。
返回结果格式示例如下。
示例 1:
输入:
Friendship 表:
+----------+----------+
| user1_id | user2_id |
+----------+----------+
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 2 | 3 |
| 2 | 4 |
| 2 | 5 |
| 6 | 1 |
+----------+----------+
Likes 表:
+---------+---------+
| user_id | page_id |
+---------+---------+
| 1 | 88 |
| 2 | 23 |
| 3 | 24 |
| 4 | 56 |
| 5 | 11 |
| 6 | 33 |
| 2 | 77 |
| 3 | 77 |
| 6 | 88 |
+---------+---------+
输出:
+---------+---------+---------------+
| user_id | page_id | friends_likes |
+---------+---------+---------------+
| 1 | 77 | 2 |
| 1 | 23 | 1 |
| 1 | 24 | 1 |
| 1 | 56 | 1 |
| 1 | 33 | 1 |
| 2 | 24 | 1 |
| 2 | 56 | 1 |
| 2 | 11 | 1 |
| 2 | 88 | 1 |
| 3 | 88 | 1 |
| 3 | 23 | 1 |
| 4 | 88 | 1 |
| 4 | 77 | 1 |
| 4 | 23 | 1 |
| 5 | 77 | 1 |
| 5 | 23 | 1 |
+---------+---------+---------------+
解释:
以用户1为例:
—用户1是用户2、3、4、6的好友。
-推荐页面有23(用户2喜欢),24(用户3喜欢),56(用户3喜欢),33(用户6喜欢),77(用户2和用户3喜欢)。
-请注意,第88页不推荐,因为用户1已经喜欢它。
另一个例子是用户6:
—用户6是用户1的好友。
-用户1只喜欢了88页,但用户6已经喜欢了。因此,用户6没有推荐。
您可以使用类似的过程为用户2、3、4和5推荐页面。
解题思路
这个问题可以通过结合两个表(Friendship
和Likes
)来解决,目标是找到对每个用户推荐的页面。具体步骤如下:
- 确定朋友关系 :首先,我们需要从
Friendship
表中识别每个用户的所有朋友。由于朋友关系是双向的(如果A是B的朋友,那么B也是A的朋友),我们需要考虑user1_id
和user2_id
两个方向。 - 识别朋友喜欢的页面:接下来,我们需要查找每个用户的朋友喜欢哪些页面。
- 排除用户已经喜欢的页面:我们需要确保推荐的页面不包括用户已经喜欢的页面。
- 计算每个推荐页面的朋友喜欢数:对于每个推荐的页面,我们需要计算有多少个朋友喜欢它。
- 输出结果 :最后,我们需要按照题目要求输出结果,包括
user_id
、page_id
和friends_likes
。
完整代码
SELECT
F.user_id,
L.page_id,
COUNT(*) AS friends_likes
FROM (
SELECT user1_id AS user_id, user2_id AS friend_id FROM Friendship
UNION ALL
SELECT user2_id, user1_id FROM Friendship
) AS F
JOIN Likes AS L ON F.friend_id = L.user_id
LEFT JOIN Likes AS UL ON F.user_id = UL.user_id AND L.page_id = UL.page_id
WHERE UL.user_id IS NULL
GROUP BY F.user_id, L.page_id
ORDER BY F.user_id, L.page_id;
- 朋友关系 :通过
UNION ALL
将Friendship
表中的user1_id
和user2_id
合并,确保朋友关系的双向性被考虑。 - 朋友喜欢的页面 :通过将上述结果与
Likes
表连接,找到每个用户的朋友喜欢哪些页面。 - 排除已喜欢的页面 :使用
LEFT JOIN
将用户喜欢的页面与朋友喜欢的页面进行比较,通过WHERE UL.user_id IS NULL
条件排除用户已经喜欢的页面。 - 计算朋友喜欢数 :通过
COUNT(*)
计算每个推荐页面的朋友喜欢数。 - 输出结果 :最后,根据
user_id
和page_id
分组,按照题目要求输出结果。
通过
优化
强调效率问题,其实在运行能力有限的情况下,讨论优化SQL是十分必要的。尽量多用left/right join + where xx is null的形式来代替not in的表示形式。
with t1 as (
select user1_id user_id
,user2_id friend_id
from Friendship
union all
select user2_id user_id
,user1_id friend_id
from Friendship
)
,t3 as (
select t1.user_id
,t1.friend_id
,t2.page_id
from t1
left join Likes t2
on t1.friend_id = t2.user_id
)
select t3.user_id
,t3.page_id
,count(1) friends_likes
from Likes t4
right join t3
on t4.user_id = t3.user_id
and t4.page_id = t3.page_id
where t4.page_id is null
group by 1,2