练习题(2024/4/12)

本文主要介绍了如何在两个数组中查找特定元素的下一个更大元素,同时提供了两种方法,一种是利用哈希映射和栈,另一种是处理循环数组。另外,还展示了如何通过SQL查询找出购买了所有产品或合作过至少三次的客户和演员/导演的ID对。
摘要由CSDN通过智能技术生成

1下一个更大元素 I

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

示例 1:

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 4 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。
- 1 ,用加粗斜体标识,nums2 = [1,3,4,2]。下一个更大元素是 3 。
- 2 ,用加粗斜体标识,nums2 = [1,3,4,2]。不存在下一个更大元素,所以答案是 -1 。

示例 2:

输入:nums1 = [2,4], nums2 = [1,2,3,4].
输出:[3,-1]
解释:nums1 中每个值的下一个更大元素如下所述:
- 2 ,用加粗斜体标识,nums2 = [1,2,3,4]。下一个更大元素是 3 。
- 4 ,用加粗斜体标识,nums2 = [1,2,3,4]。不存在下一个更大元素,所以答案是 -1 。

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 104
  • nums1nums2中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2 中

思路:

这个问题的目标是给定两个数组 nums1 和 nums2,其中 nums1 是 nums2 的子集。对于 nums1 中的每个元素,在 nums2 中找到这个元素右边的第一个比它大的数,并返回其索引,如果不存在则返回 -1。

这里的解决思路是利用栈和哈希映射来实现。具体来说:

  1. 首先,遍历 nums1 数组,将每个元素及其在 nums1 中的索引存储到哈希映射 Imap 中。

  2. 然后,利用栈 st 来遍历 nums2 数组。栈中存放的是 nums2 数组元素的索引。

  3. 在遍历 nums2 数组的过程中,如果当前元素比栈顶元素大,就说明找到了栈顶元素右边的第一个比它大的数。此时,通过哈希映射 Imap 找到这个数在 nums1 中的索引,并将结果存储到 result 数组中。

  4. 如果当前元素不大于栈顶元素,则将当前元素的索引压入栈中,继续遍历下一个元素。

  5. 最后,返回 result 数组,其中存放着 nums1 中每个元素的下一个更大的数的索引。

代码:

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        stack<int> st; // 创建一个栈,用于存储nums2中的索引
        vector<int> result(nums1.size(), -1); // 创建一个大小与nums1相同的结果向量,初始化为-1
        if(nums1.size() == 0) return result; // 如果nums1为空,直接返回结果向量

        map<int, int> Imap; // 创建一个map,用于存储nums1中每个元素对应的索引
        for(int i = 0; i < nums1.size(); i++) {
            Imap[nums1[i]] = i; // 将nums1中每个元素及其索引存入map中
        }

        st.push(0); // 将nums2的第一个元素的索引压入栈中
        for(int i = 1; i < nums2.size(); i++) {
            // 遍历nums2中的元素
            while(!st.empty() && nums2[i] > nums2[st.top()]) {
                // 如果栈不为空且当前元素大于栈顶元素
                if(Imap.count(nums2[st.top()]) > 0) {
                    // 如果当前栈顶元素在map中存在对应的索引
                    int index = Imap[nums2[st.top()]]; // 获取当前栈顶元素在nums1中的索引
                    result[index] = nums2[i]; // 更新结果向量中对应位置的值为当前元素
                }
                st.pop(); // 弹出栈顶元素
            }
            st.push(i); // 将当前元素的索引压入栈中
        }

        return result; // 返回结果向量
    }
};

2下一个更大元素 II

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

示例 1:

输入: nums = [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数; 
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

示例 2:

输入: nums = [1,2,3,4,3]
输出: [2,3,4,-1,4]

提示:

  • 1 <= nums.length <= 104
  • -109 <= nums[i] <= 109

思路1:

  1. 首先,创建一个与原数组相同的副本,并将其连接到原数组的末尾,这样可以模拟循环数组的效果。

  2. 创建一个大小与数组相同的结果向量,用于存储每个元素的下一个更大的元素。初始化结果向量为-1。

  3. 如果数组为空,直接返回结果向量。

  4. 创建一个栈用于存储数组元素的索引,并将第一个元素的索引压入栈中。

  5. 遍历数组中的元素,从第二个元素开始:

    • 如果栈不为空且当前元素大于栈顶元素所对应的元素,则将栈顶元素所对应的结果向量位置更新为当前元素,并弹出栈顶元素,直到栈为空或者当前元素不大于栈顶元素所对应的元素。

    • 将当前元素的索引压入栈中。

  6. 遍历完所有元素后,将结果向量resize到原始数组的大小,即去掉副本部分。

代码1:


class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        // 创建一个与nums相同的副本nums1,并将其连接到nums后面
        vector<int> nums1(nums.begin(), nums.end());
        nums.insert(nums.end(), nums1.begin(), nums1.end());

        // 创建一个大小与nums相同的结果向量,初始化为-1
        vector<int> result(nums.size(), -1);

        // 如果nums为空,直接返回结果向量
        if (nums.size() == 0) return result;

        // 创建一个栈用于存储nums中元素的索引
        stack<int> st;
        st.push(0);

        // 遍历nums中的元素
        for (int i = 1; i < nums.size(); i++) {
            // 如果栈不为空且当前元素大于栈顶元素
            while (!st.empty() && nums[i] > nums[st.top()]) {
                // 将栈顶元素右边第一个比它大的数存储到结果向量中
                result[st.top()] = nums[i];
                // 弹出栈顶元素
                st.pop();
            }
            // 将当前元素的索引压入栈中
            st.push(i);
        }

        // 将结果向量resize到原始nums的大小
        result.resize(nums.size() / 2);
        return result;
    }
};

取模思路2:

  1. 创建一个大小与原始数组相同的结果向量,初始化为-1。

  2. 如果数组为空,直接返回结果向量。

  3. 创建一个栈用于存储数组元素的索引,并将第一个元素的索引压入栈中。

  4. 模拟遍历两次原始数组,这样可以处理循环数组的情况。遍历过程中,通过取模操作来访问数组元素,使得索引在数组长度范围内循环。

  5. 对于每个遍历到的元素,如果当前元素小于栈顶元素所对应的元素,则将当前元素的索引压入栈中;如果当前元素等于栈顶元素所对应的元素,也将当前元素的索引压入栈中;如果当前元素大于栈顶元素所对应的元素,则更新结果向量并弹出栈顶元素,直到栈为空或者当前元素不大于栈顶元素所对应的元素。

  6. 最后,返回结果向量。

(知识重点取模过程)当我们遍历两次数组时,我们需要模拟循环数组的效果。在遍历过程中,我们使用取模操作来访问数组元素,使得索引在数组长度范围内循环。

具体来说,假设原始数组的长度为 n,我们将数组的长度扩展为 2n。这样,当我们遍历到第 n 个元素时,我们会开始访问数组的第二轮,即实际上访问的是原始数组中的元素。

举个例子,假设原始数组为 [1, 2, 3, 4, 5],那么扩展后的数组为 [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]。在遍历过程中,当索引 i 递增到 5 时,我们通过取模操作 i % n 实际上访问的是数组的第一个元素 nums[5 % 5] = nums[0] = 1

通过这种方式,我们可以在遍历过程中模拟循环数组的效果,从而正确地找到每个元素的下一个更大的元素。

代码2:

class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        // 创建一个与nums相同大小的结果向量,初始化为-1
        vector<int> result(nums.size(), -1);
        // 如果nums为空,直接返回结果向量
        if (nums.size() == 0) return result;
        
        // 创建一个栈用于存储数组元素的索引
        stack<int> st;
        // 将第一个元素的索引压入栈中
        st.push(0);
        
        // 模拟遍历两次nums数组
        for (int i = 1; i < nums.size() * 2; i++) {
            // 使用取模操作来模拟循环数组的效果
            // 如果当前元素小于栈顶元素所对应的元素,将当前元素的索引压入栈中
            if (nums[i % nums.size()] < nums[st.top()]) st.push(i % nums.size());
            // 如果当前元素等于栈顶元素所对应的元素,也将当前元素的索引压入栈中
            else if (nums[i % nums.size()] == nums[st.top()]) st.push(i % nums.size());
            // 如果当前元素大于栈顶元素所对应的元素,更新结果向量并弹出栈顶元素,直到栈为空或者当前元素不大于栈顶元素所对应的元素
            else {
                while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {
                    result[st.top()] = nums[i % nums.size()];
                    st.pop();
                }
                // 将当前元素的索引压入栈中
                st.push(i % nums.size());
            }
        }
        
        return result;
    }
};

3 买下所有产品的客户

Customer 表:

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| customer_id | int     |
| product_key | int     |
+-------------+---------+
该表可能包含重复的行。
customer_id 不为 NULL。
product_key 是 Product 表的外键(reference 列)。

Product 表:

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| product_key | int     |
+-------------+---------+
product_key 是这张表的主键(具有唯一值的列)。

编写解决方案,报告 Customer 表中购买了 Product 表中所有产品的客户的 id。

返回结果表 无顺序要求 。

返回结果格式如下所示。

示例 1:

输入:
Customer 表:
+-------------+-------------+
| customer_id | product_key |
+-------------+-------------+
| 1           | 5           |
| 2           | 6           |
| 3           | 5           |
| 3           | 6           |
| 1           | 6           |
+-------------+-------------+
Product 表:
+-------------+
| product_key |
+-------------+
| 5           |
| 6           |
+-------------+
输出:
+-------------+
| customer_id |
+-------------+
| 1           |
| 3           |
+-------------+
解释:
购买了所有产品(5 和 6)的客户的 id 是 1 和 3 。

思路:

  1. 分组客户ID:首先,我们使用 group by customer_id 将客户表按照客户ID进行分组。这样,每个客户ID都形成一个分组。

  2. 计算每个客户购买的产品数量:在每个分组中,我们使用 couunt(distinct product_key) 来计算每个客户购买的不同产品的数量。这里使用 distinct 关键词确保我们不会重复计算相同的产品。

  3. 计算产品总数:为了确定购买了所有产品的客户,我们需要知道产品的总数。为此,我们使用一个子查询 (select count(*) from  Product) 来计算产品表中所有行的数量,即总产品数。

  4. 筛选符合条件的分组:通过 having count (distinct product_key) = (...) 条件,我们筛选出购买了所有不同产品的客户。这个条件比较每个客户购买的产品数量与总产品数是否相等。

  5. 返回客户ID:最后,我们通过 select  customer_id 来选择要返回的结果,即购买了所有产品的客户ID。

代码 :

-- 查询出购买了所有产品的客户ID
select customer_id  -- 选择要返回的结果,即客户ID
from customer  -- 从Customer表中进行查询
group by customer_id  -- 按照客户ID进行分组,每个客户ID形成一个分组
having count(distinct product_key) = (  -- 使用HAVING子句筛选符合条件的分组
    -- 子查询:计算产品总数
    select count(*)  -- 计算产品表中所有行的数量
    from product  -- 从Product表中进行查询
);

4合作过至少三次的演员和导演

ActorDirector 表:

+-------------+---------+
| Column Name | Type    |
+-------------+---------+
| actor_id    | int     |
| director_id | int     |
| timestamp   | int     |
+-------------+---------+
timestamp 是这张表的主键(具有唯一值的列).

编写解决方案找出合作过至少三次的演员和导演的 id 对 (actor_id, director_id)

示例 1:

输入:
ActorDirector 表:
+-------------+-------------+-------------+
| actor_id    | director_id | timestamp   |
+-------------+-------------+-------------+
| 1           | 1           | 0           |
| 1           | 1           | 1           |
| 1           | 1           | 2           |
| 1           | 2           | 3           |
| 1           | 2           | 4           |
| 2           | 1           | 5           |
| 2           | 1           | 6           |
+-------------+-------------+-------------+
输出:
+-------------+-------------+
| actor_id    | director_id |
+-------------+-------------+
| 1           | 1           |
+-------------+-------------+
解释:
唯一的 id 对是 (1, 1),他们恰好合作了 3 次。

思路:

这个查询的具体思路如下:

  1. 分组演员和导演:首先,我们使用 group by actor_id, director_id 将 ActorDirector 表按照演员ID和导演ID进行分组。这样,每个演员和导演的组合都形成一个分组。

  2. 计算合作次数:在每个分组中,我们使用 count(timestamp) 来计算每个演员和导演的合作次数。这里使用 COUNT 函数来统计每个分组中的记录数量,即合作次数。

  3. 筛选合作次数大于等于3次的组:通过 having count (timestamp) >= 3 条件,我们筛选出合作次数大于等于3次的演员和导演组合。这个条件比较每个组合的合作次数是否满足要求。

  4. 返回演员ID和导演ID:最后,我们通过 select actor_id, director_id 来选择要返回的结果,即合作次数大于等于3次的演员和导演的ID组合。

代码:

​
#-- 查询演员和导演在ActorDirector表中合作的次数大于等于3次的记录,返回演员ID和导演ID
select actor_id ,director_id 
from ActorDirector  #-- 从ActorDirector表中进行查询
group by actor_id ,director_id  #-- 按照演员ID和导演ID进行分组,每个组代表一次合作
having count(timestamp)>=3 # -- 使用HAVING子句筛选出合作次数大于等于3次的组

​

5 产品销售分析 I

销售表 Sales

+-------------+-------+
| Column Name | Type  |
+-------------+-------+
| sale_id     | int   |
| product_id  | int   |
| year        | int   |
| quantity    | int   |
| price       | int   |
+-------------+-------+
(sale_id, year) 是销售表 Sales 的主键(具有唯一值的列的组合)。
product_id 是关联到产品表 Product 的外键(reference 列)。
该表的每一行显示 product_id 在某一年的销售情况。
注意: price 表示每单位价格。

产品表 Product

+--------------+---------+
| Column Name  | Type    |
+--------------+---------+
| product_id   | int     |
| product_name | varchar |
+--------------+---------+
product_id 是表的主键(具有唯一值的列)。
该表的每一行表示每种产品的产品名称。

编写解决方案,以获取 Sales 表中所有 sale_id 对应的 product_name 以及该产品的所有 year 和 price 。

返回结果表 无顺序要求 。

结果格式示例如下。

示例 1:

输入:
Sales 表:
+---------+------------+------+----------+-------+
| sale_id | product_id | year | quantity | price |
+---------+------------+------+----------+-------+ 
| 1       | 100        | 2008 | 10       | 5000  |
| 2       | 100        | 2009 | 12       | 5000  |
| 7       | 200        | 2011 | 15       | 9000  |
+---------+------------+------+----------+-------+
Product 表:
+------------+--------------+
| product_id | product_name |
+------------+--------------+
| 100        | Nokia        |
| 200        | Apple        |
| 300        | Samsung      |
+------------+--------------+
输出:
+--------------+-------+-------+
| product_name | year  | price |
+--------------+-------+-------+
| Nokia        | 2008  | 5000  |
| Nokia        | 2009  | 5000  |
| Apple        | 2011  | 9000  |
+--------------+-------+-------+

思路:

  1. 连接表格:首先,我们使用 join 将 Sales 表和 Product 表连接起来。这样,我们可以将销售记录与对应的产品信息关联起来。

  2. 选择列:然后,我们使用 select语句选择需要显示的列,包括产品名称、销售年份和价格。

  3. 设置别名:为了简化查询语句,我们使用 AS 关键字为表设置别名,分别为 Sales 表设置别名为 s,Product 表设置别名为 p

  4. 连接条件:在 ON 子句中,我们使用 s.product_id = p.product_id 来指定连接条件,确保我们根据产品ID将两个表连接起来,这样每条销售记录都与对应的产品信息关联。

代码:


select p.product_name,s.year,s.price

from

    Sales as s join Product as p 
on 
    s.product_id=p.product_id

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值