1.题目介绍
题目地址(1630. 等差子数组 - 力扣(LeetCode))
https://leetcode.cn/problems/arithmetic-subarrays/
题目描述
如果一个数列由至少两个元素组成,且每两个连续元素之间的差值都相同,那么这个序列就是 等差数列 。更正式地,数列 s
是等差数列,只需要满足:对于每个有效的 i
, s[i+1] - s[i] == s[1] - s[0]
都成立。
例如,下面这些都是 等差数列 :
1, 3, 5, 7, 9 7, 7, 7, 7 3, -1, -5, -9
下面的数列 不是等差数列 :
1, 1, 2, 5, 7
给你一个由 n
个整数组成的数组 nums
,和两个由 m
个整数组成的数组 l
和 r
,后两个数组表示 m
组范围查询,其中第 i
个查询对应范围 [l[i], r[i]]
。所有数组的下标都是 从 0 开始 的。
返回 boolean
元素构成的答案列表 answer
。如果子数组 nums[l[i]], nums[l[i]+1], ... , nums[r[i]]
可以 重新排列 形成 等差数列 ,answer[i]
的值就是 true
;否则answer[i]
的值就是 false
。
示例 1:
输入:nums =[4,6,5,9,3,7]
, l =[0,0,2]
, r =[2,3,5]
输出:[true,false,true]
解释: 第 0 个查询,对应子数组 [4,6,5] 。可以重新排列为等差数列 [6,5,4] 。 第 1 个查询,对应子数组 [4,6,5,9] 。无法重新排列形成等差数列。 第 2 个查询,对应子数组[5,9,3,7] 。
可以重新排列为等差数列[3,5,7,9] 。
示例 2:
输入:nums = [-12,-9,-3,-12,-6,15,20,-25,-20,-15,-10], l = [0,1,6,4,8,7], r = [4,4,9,7,9,10] 输出:[false,true,false,false,true,true]
提示:
n == nums.length
m == l.length
m == r.length
2 <= n <= 500
1 <= m <= 500
0 <= l[i] < r[i] < n
-105 <= nums[i] <= 105
2.题解
2.1暴力枚举
思路
很简单,将数组根据l和r的要求进行拆分排序,然后从头到尾进行遍历,判断是否为等差数组
代码
- 语言支持:C++
C++ Code:
class Solution {
public:
vector<bool> checkArithmeticSubarrays(vector<int>& nums, vector<int>& l,vector<int>& r) {
int m = l.size();
vector<bool> ans;
for (int i = 0; i < m; i++) {
vector<int> subNums(nums.begin() + l[i], nums.begin() + r[i] + 1);
sort(subNums.begin(), subNums.end());
int n = subNums.size();
bool flag = true;
if (n == 2) {
ans.push_back(flag);
continue;
}
int sub = subNums[1] - subNums[0];
for (int j = 2; j < n; j++) {
if (subNums[j] - subNums[j - 1] != sub) {
flag = false;
break;
}
}
ans.push_back(flag);
}
return ans;
}
};
复杂度分析
- 时间复杂度:每个子数组的排序操作为‘o(k log k)’,其中‘k 是子数组的长度。
由于总共有“m个查询,所以总时间复杂度为”o(m*k log k)”,最坏情况下为\(O(m*n\log n)^{\circ}\)。 - 空间复杂度:需要额外的空间来存储每个子数组,最坏情况下总空间复杂度为\(\because O(n)^{\circ}\)。
这个解决方案对于给定的限制是高效的,因为 n 和 m 都是 500 以内,排序操作在这个范围内是可以接受的。
2.2 优化(判断等差数列——去除排序步骤)
思路
对于每一个子数组,我们考虑不进行排序判断其是否为等差数列
第一次遍历,确定其最大值,最小值;通过这两个值我们可以判断该数组如果为等差,那么他的公差为
代码
class Solution {
public:
vector<bool> checkArithmeticSubarrays(vector<int>& nums, vector<int>& l,vector<int>& r) {
int m = l.size();
vector<bool> ans;
for (int i = 0; i < m; i++) {
int start = l[i], end = r[i];
int n = end - start + 1; // 子数组总个数
int max_value = INT_MIN, min_value = INT_MAX;
bool isArithmetic = true;
// 子数组长度为2则必为等差数列
if(n == 2){
ans.push_back(isArithmetic);
continue;
}
// 遍历子数组,获得最大值,最小值
for(int j = start; j <= end; j++){
max_value = max(max_value, nums[j]);
min_value = min(min_value, nums[j]);
}
// 判断子数组是否为等差数列
if((max_value - min_value) % (n - 1) != 0){
isArithmetic = false;
ans.push_back(isArithmetic);
continue;
}
// 计算公差
int diff = (max_value - min_value) / (n - 1);
if(diff == 0){
ans.push_back(isArithmetic);
continue;
}
vector<bool> visitor(n, false); // 判断是否遇到过该数
// 遍历数组
for(int j = start; j <= end; j++){
if((nums[j] - min_value) % diff != 0){
isArithmetic = false;
break;
}
int pos = (nums[j] - min_value) / diff;
if(pos >= n || visitor[pos]){
isArithmetic = false;
break;
}
visitor[pos] = true;
}
ans.push_back(isArithmetic);
}
return ans;
}
};
复杂度分析
- 时间复杂度:
每次查询中,我们需要遍历子数组找最小值和最大值,这需要\(o(k)\)”时间。·然后我们需要再次遍历子数组来判断每个数字的位置,最坏情况下总共需要\(\because o(k)^{\circ}时间\)。
所以每个查询的时间复杂度是“o(k)”。
总共有“m个查询,所以总时间复杂度为’o(m*k)’最坏情况下为\(o(n * m)\) - 空间复杂度:
主要是用于记录每个数字位置的布尔数组‘seen’,其大小为子数组的长度,最坏情况下为“o(n)”。总的空间复杂度为 'o(n) 。