LeetCode 1341, 15, 543

1341. 电影评分

题目链接

1341. 电影评分

  • Movies的字段为movie_idtitle
  • Users的字段为user_idname
  • MovieRating的字段为movie_iduser_idratingcreated_at

要求

请你编写一个解决方案:

  • 查找评论电影数量最多的用户名。如果出现平局,返回字典序较小的用户名。
  • 查找在 February 2020 平均评分最高 的电影名称。如果出现平局,返回字典序较小的电影名称。

字典序 ,即按字母在字典中出现顺序对字符串排序,字典序较小则意味着排序靠前。

知识点

  1. count():统计个数的函数。
  2. avg():求平均值的函数。
  3. group by:根据某些字段分组。
  4. order by:根据某些字段排序。
  5. between and:将字段的值限制到闭区间内。
  6. limit:限制返回的数据的条数。
  7. 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. 三数之和

题目链接

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. 二叉树的直径

题目链接

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; // 返回当前节点的深度
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值