目录
1341. 电影评分
题目链接
表
- 表
Movies
的字段为movie_id
和title
。 - 表
Users
的字段为user_id
和name
。 - 表
MovieRating
的字段为movie_id
、user_id
、rating
和created_at
。
要求
请你编写一个解决方案:
- 查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。
- 查找在
February 2020
平均评分最高 的电影名称。如果出现平局,返回字典序较小的电影名称。
字典序 ,即按字母在字典中出现顺序对字符串排序,字典序较小则意味着排序靠前。
知识点
count()
:统计个数的函数。avg()
:求平均值的函数。group by
:根据某些字段分组。order by
:根据某些字段排序。between and
:将字段的值限制到闭区间内。limit
:限制返回的数据的条数。union all
:将查询到的结果联合到一起。
思路
本题只是求解的步骤比较多,但并不复杂。它由两个查询构成:第一个是查询评论电影数量最多的用户名,第二个是查询在 February 2020
平均评分最高 的电影名称。可以将其分别求出来,然后使用union all
进行汇总。
查询评论电影数量最多的用户名
要查询评论电影数量最多的用户名,需要先查询每个用户评论电影的数量num
,然后与用户表Users
进行多表查询,将用户按评论电影的数量num
降序desc
排列,当num
相同时,再根据用户名name
升序排列,最后返回第一条数据limit 1
即可。
查询在 February 2020 平均评分最高 的电影名称
要查询在 February 2020
平均评分最高 的电影名称,需要先查询每部电影在February 2020
的平均分avg_rating
,然后与电影表Movies
进行多表查询,将电影按在February 2020
的平均分avg_rating
降序desc
排列,当avg_rating
相同时,再根据电影名title
升序排列,最后返回第一条数据limit 1
即可。
代码
(
select
u.name results
from
Users u,
(
select
count(*) num,
user_id
from
MovieRating
group by
user_id
) cnt
where
u.user_id = cnt.user_id
order by
cnt.num desc,
u.name
limit 1
) union all (
select
m.title results
from
Movies m,
(
select
avg(rating) avg_rating,
movie_id
from
MovieRating
where
created_at
between
'2020-02-01'
and
'2020-02-29'
group by
movie_id
) cnt
where
m.movie_id = cnt.movie_id
order by
cnt.avg_rating desc,
m.title
limit 1
)
15. 三数之和
题目链接
标签
数组 双指针 排序
思路
如果将数组中的元素按升序排列,那么三数之和可以转化为枚举第一个数nums[i]
后再求两数之和为-nums[i]
的两个索引。
求两数之和为-nums[i]
的两个索引很简单,只需要使用两个指针j, k
,分别指向数组的“头”(这里的“头”指的是 第一个数右边 到 数组结尾这个区间的第一个元素)和尾,j
正向遍历,k
反向遍历,直到j, k
相遇,这里不采用正规的求两数之和的做法,而是像将三数之和转化为两数之和一样先枚举第二个数,然后再搜索第三个数,这是因为数组中的元素可能重复,如果两个指针都动,那么就很难找到不重复的解。
但本题的nums
数组中的元素可能重复,不能直接寻找。可以思考一下,怎样使和一定的三个数不重复(这里的三个数不重复不是三个数都不同,而是结果集合中不能找到含有相同三个数的两个集合)?答案是让其中的两个数不同。所以本题只需要移动前两个数的索引i, j
即可,但在移动时得保证这个元素与上一个元素不重复。
代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
// 对数组进行排序
Arrays.sort(nums);
int n = nums.length;
List<List<Integer>> ans = new ArrayList<>();
for (int i = 0; i < n; i++) { // i是第一个数的索引
// 寻找一个不重复的第一个数的索引,如果找不到,则退出循环
i = findNonDuplicateIndex(nums, 0, i);
if (-1 == i) {
break;
}
int j = i + 1; // j是第二个数的索引
int k = n - 1; // k是第三个数的索引
int tar = -nums[i]; // 待寻找值(目标值)
while (j < n) {
// 寻找一个不重复的第二个数的索引,如果找不到,则退出循环
j = findNonDuplicateIndex(nums, i + 1, j);
if (-1 == j) {
break;
}
// 搜索两数之和不大于tar的第三个数的索引
while (j < k && nums[j] + nums[k] > tar) {
k--;
}
// 如果第二个数的索引超越了第三个数的索引,直接跳出内层循环
if (j >= k) {
break;
}
// 如果找到合适的三个数的索引,将三个数放入ans中
if (nums[j] + nums[k] == tar) {
ans.add(List.of(nums[i], nums[j], nums[k]));
}
// 寻找下一组两数之和为tar的索引
j++;
}
}
return ans;
}
// 初始值为left,从index开始,寻找不与上一个值重复的索引,如果找不到,则返回-1
private int findNonDuplicateIndex(int[] nums, int left, int index) {
if (left == index) { // 如果index就是初始值,则它没有上一个值
return index; // 直接返回它
}
while (index < nums.length) {
if (nums[index] != nums[index - 1]) { // 如果index指向的值与它的上一个值不相等
return index; // 则返回它
}
index++;
}
return -1; // 如果找不到,则返回-1
}
}
543. 二叉树的直径
题目链接
标签
树 深度优先搜索 二叉树
思路
对于一个节点来说,从 它左子树的最底层节点 到 它右子树的最底层节点 的长度最长,题目中的直径从每个节点的最长长度中取最大值得到。
因为在求每个节点的最长长度时会求左子树的深度和右子树的深度,所以本题可以看作是求二叉树的深度的副产物。
求二叉树的深度很简单,看这篇文章104. 二叉树的最大深度。
代码
class Solution {
public int diameterOfBinaryTree(TreeNode root) {
int[] ans = {0}; // 给方法传入 引用类型的数据 时可以修改数据的值
depth(root, ans);
return ans[0];
}
// 求本节点的深度
private int depth(TreeNode curr, int[] ans) {
if (curr == null) { // 如果当前节点为null
return 0; // 则它的高度为0
}
int left = depth(curr.left, ans); // 左子树的深度
int right = depth(curr.right, ans); // 右子树的深度
// 让ans取 上一个ans 和 从左子树底到右子树底的长度 的较大值
ans[0] = Math.max(ans[0], left + right); // 这就是副产物
return Math.max(left, right) + 1; // 返回当前节点的深度
}
}