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
nums1
和nums2
中所有整数 互不相同nums1
中的所有整数同样出现在nums2
中
思路:
这个问题的目标是给定两个数组 nums1
和 nums2
,其中 nums1
是 nums2
的子集。对于 nums1
中的每个元素,在 nums2
中找到这个元素右边的第一个比它大的数,并返回其索引,如果不存在则返回 -1。
这里的解决思路是利用栈和哈希映射来实现。具体来说:
-
首先,遍历
nums1
数组,将每个元素及其在nums1
中的索引存储到哈希映射Imap
中。 -
然后,利用栈
st
来遍历nums2
数组。栈中存放的是nums2
数组元素的索引。 -
在遍历
nums2
数组的过程中,如果当前元素比栈顶元素大,就说明找到了栈顶元素右边的第一个比它大的数。此时,通过哈希映射Imap
找到这个数在nums1
中的索引,并将结果存储到result
数组中。 -
如果当前元素不大于栈顶元素,则将当前元素的索引压入栈中,继续遍历下一个元素。
-
最后,返回
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。
-
如果数组为空,直接返回结果向量。
-
创建一个栈用于存储数组元素的索引,并将第一个元素的索引压入栈中。
-
遍历数组中的元素,从第二个元素开始:
-
如果栈不为空且当前元素大于栈顶元素所对应的元素,则将栈顶元素所对应的结果向量位置更新为当前元素,并弹出栈顶元素,直到栈为空或者当前元素不大于栈顶元素所对应的元素。
-
将当前元素的索引压入栈中。
-
-
遍历完所有元素后,将结果向量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。
-
如果数组为空,直接返回结果向量。
-
创建一个栈用于存储数组元素的索引,并将第一个元素的索引压入栈中。
-
模拟遍历两次原始数组,这样可以处理循环数组的情况。遍历过程中,通过取模操作来访问数组元素,使得索引在数组长度范围内循环。
-
对于每个遍历到的元素,如果当前元素小于栈顶元素所对应的元素,则将当前元素的索引压入栈中;如果当前元素等于栈顶元素所对应的元素,也将当前元素的索引压入栈中;如果当前元素大于栈顶元素所对应的元素,则更新结果向量并弹出栈顶元素,直到栈为空或者当前元素不大于栈顶元素所对应的元素。
-
最后,返回结果向量。
(知识重点取模过程)当我们遍历两次数组时,我们需要模拟循环数组的效果。在遍历过程中,我们使用取模操作来访问数组元素,使得索引在数组长度范围内循环。
具体来说,假设原始数组的长度为 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 。
思路:
-
分组客户ID:首先,我们使用 group by
customer_id
将客户表按照客户ID进行分组。这样,每个客户ID都形成一个分组。 -
计算每个客户购买的产品数量:在每个分组中,我们使用 couunt
(distinct product_key)
来计算每个客户购买的不同产品的数量。这里使用 distinct 关键词确保我们不会重复计算相同的产品。 -
计算产品总数:为了确定购买了所有产品的客户,我们需要知道产品的总数。为此,我们使用一个子查询
(select count(*) from Product)
来计算产品表中所有行的数量,即总产品数。 -
筛选符合条件的分组:通过 having count
(distinct product_key) = (...)
条件,我们筛选出购买了所有不同产品的客户。这个条件比较每个客户购买的产品数量与总产品数是否相等。 -
返回客户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 次。
思路:
这个查询的具体思路如下:
-
分组演员和导演:首先,我们使用 group by
actor_id, director_id
将 ActorDirector 表按照演员ID和导演ID进行分组。这样,每个演员和导演的组合都形成一个分组。 -
计算合作次数:在每个分组中,我们使用 count
(timestamp)
来计算每个演员和导演的合作次数。这里使用COUNT
函数来统计每个分组中的记录数量,即合作次数。 -
筛选合作次数大于等于3次的组:通过 having count
(timestamp) >= 3
条件,我们筛选出合作次数大于等于3次的演员和导演组合。这个条件比较每个组合的合作次数是否满足要求。 -
返回演员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 |
+--------------+-------+-------+
思路:
-
连接表格:首先,我们使用 join 将 Sales 表和 Product 表连接起来。这样,我们可以将销售记录与对应的产品信息关联起来。
-
选择列:然后,我们使用 select语句选择需要显示的列,包括产品名称、销售年份和价格。
-
设置别名:为了简化查询语句,我们使用
AS
关键字为表设置别名,分别为 Sales 表设置别名为s
,Product 表设置别名为p
。 -
连接条件:在
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