LeetCode 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (绝对差不超过限制的最长连续子数组)
给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit 。
如果不存在满足条件的子数组,则返回 0 。
示例 1:
输入:nums = [8,2,4,7], limit = 4
输出:2
解释:所有子数组如下:
[8] 最大绝对差 |8-8| = 0 <= 4.
[8,2] 最大绝对差 |8-2| = 6 > 4.
[8,2,4] 最大绝对差 |8-2| = 6 > 4.
[8,2,4,7] 最大绝对差 |8-2| = 6 > 4.
[2] 最大绝对差 |2-2| = 0 <= 4.
[2,4] 最大绝对差 |2-4| = 2 <= 4.
[2,4,7] 最大绝对差 |2-7| = 5 > 4.
[4] 最大绝对差 |4-4| = 0 <= 4.
[4,7] 最大绝对差 |4-7| = 3 <= 4.
[7] 最大绝对差 |7-7| = 0 <= 4.
因此,满足题意的最长子数组的长度为 2 。
比赛时这破题真就卡了一个小时= = 然后第四题没来记得看= = 我好菜啊
解法1、树状数组/线段树 求区间极值 + 二分
树状数组求区间最大最小值 logn 二分长度然后枚举起点 时间复杂度 O(logn * logn * n)
线段树 太久没写了 练下手。不过这题比较简单,没有更新,没有什么pushup pushdown的操作
#define lson (o<<1)
#define rson (o<<1|1)
#define mid ((l+r)>>1) const int N = 100005;
int max_tr[N * 3], min_tr[N * 3]; class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int n = nums.size();
build(1, 1, n, nums);
int l = 1, r = n;
int ans = 0;
// 二分长度
while (l <= r) {
int m = (l + r) >> 1;
bool f = false;
for (int i = 0, j; (j = i + m - 1) < n; i++) {
int maxv = query_max(1, 1, n, i + 1, j + 1);
int minv = query_min(1, 1, n, i + 1, j + 1);
if (maxv - minv <= limit) {
f = true;
break;
}
}
if (f) ans = m, l = m + 1;
else r = m - 1;
}
return ans;
}
void build(int o, int l, int r, vector<int>& nums) {
if (l == r) {
max_tr[o] = min_tr[o] = nums[l - 1];
return ;
}
build(lson, l, mid, nums);
build(rson, mid + 1, r, nums);
pushup(o);
}
void pushup(int o) {
max_tr[o] = max(max_tr[lson], max_tr[rson]);
min_tr[o] = min(min_tr[lson], min_tr[rson]);
}
int query_min(int o, int l, int r, int L, int R) {
if (l >= L && r <= R) return min_tr[o];
int res = INT_MAX;
if (L <= mid) res = min(res, query_min(lson, l, mid, L, R));
if (R > mid) res = min(res, query_min(rson, mid + 1, r, L, R));
return res;
}
int query_max(int o, int l, int r, int L, int R) {
if (l >= L && r <= R) return max_tr[o];
int res = 0;
if (L <= mid) res = max(res, query_max(lson, l, mid, L, R));
if (R > mid) res = max(res, query_max(rson, mid + 1, r, L, R));
return res;
}
};
树状数组 求区间极值我一直不会。。。so。。比赛时现搜的代码,然后找了一个错误的,调了半个小时。。。又换了一个博客才AC。QAQ
const int N = 100005;
int arrmi[N], arrmx[N]; class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int n = nums.size();
init(nums);
int l = 1, r = n;
int ans = 0;
// 二分长度
while (l <= r) {
int m = (l + r) >> 1;
bool f = false;
for (int i = 0, j; (j = i + m - 1) < n; i++) {
int maxv = querymx(i + 1, j + 1, nums);
int minv = querymi(i + 1, j + 1, nums);
if (maxv - minv <= limit) {
f = true;
break;
}
}
if (f) ans = m, l = m + 1;
else r = m - 1;
}
return ans;
}
void init(vector<int>& nums) {
for(int i = 1; i <= nums.size(); ++i) arrmi[i] = INT_MAX;
for(int i = 1; i <= nums.size(); ++i)
for(int j = i; j <= nums.size() && arrmi[j] > nums[i - 1]; j += lowbit(j))
arrmi[j] = nums[i - 1]; memset(arrmx, 0, sizeof arrmx);
for(int i = 1; i <= nums.size(); ++i)
for(int j = i; j <= nums.size() && arrmx[j] < nums[i - 1]; j += lowbit(j))
arrmx[j] = nums[i - 1];
}
int querymx(int L, int R, vector<int>& nums) {
int res = 0;
for (--L; L < R; ){
if (R - lowbit(R) >= L) {
res = max(res, arrmx[R]); R -= lowbit(R);
} else {
res = max(res, nums[R - 1]);
--R;
}
}
return res;
}
int querymi(int L, int R, vector<int>& nums) {
int res = INT_MAX;
for (--L; L < R; ){
if (R - lowbit(R) >= L) {
res = min(res, arrmi[R]); R -= lowbit(R);
} else {
res = min(res, nums[R - 1]);
--R;
}
}
return res;
}
int lowbit(int x) { return x & -x; }
};
树状数组(1032 ms)比线段树(1944 ms)快一倍
解法2、multiset + 滑动窗口
我只能说STL是真的好用,但是我不太会用啊。。。。
multiset 能在 logn 的事件复杂度求出最大最小值,也能在 O(logn) 的复杂度增加删除元素
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int n = nums.size();
multiset<int> st;
int l = 0, r = 0, ans = 0;
while (r < n) {
st.insert(nums[r++]);
while (*st.rbegin() - *st.begin() > limit) {
st.erase(st.find(nums[l++]));
}
ans = max(ans, r - l);
}
return ans;
}
};
md 10行代码,我看着我比赛写的100行代码真的好气。。然后只需要 260 ms。。。
解法3、双端队列 + 滑动窗口
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
int n = nums.size();
deque<int> deq; // 降序队列 记录最大值
deque<int> inq; // 升序队列 记录最小值
int l = 0, r = 0, ans = 0;
while (r < n) {
int x = nums[r];
while (deq.size() && nums[deq.back()] <= x) {
deq.pop_back();
}
deq.push_back(r);
while (inq.size() && nums[inq.back()] >= x) {
inq.pop_back();
}
inq.push_back(r);
while (nums[deq.front()] - nums[inq.front()] > limit) {
l++;
if (deq.front() < l) deq.pop_front();
if (inq.front() < l) inq.pop_front();
}
ans = max(ans, r - l + 1);
r++;
}
return ans;
}
};
感觉这种解法更妙一些,应该是 O(n) 的时间复杂度吧。132 ms 我之前应该是写过这种题,但是应该是很久以前了,只有浅浅的印象==
用两个双端队列记录每一个对最大最小值有贡献的位置。
比如输入 [8,2,4,7] 4
初始化,l=0, r=0
deq [0] inq [0] // 都是[8]
最大值-最小值 = 8-8 = 0 <= 4 所以 子数组 [0,0] 长度为 1
右端向右移动,r=1
deq [0,1] inq [1] // 下标对应值 deq [8,2] inq [2]
对于inq,要知道计算是从前到后的 1 这个位置比 0 小,所以 0 这个位置对于最小值是没有贡献的,如果你想向前移动到最小值变大,一定要移动到 1 后面。这就是 inq 记录的下标的意义。
而对于 deq 我们记录的是对最大值右贡献的位置,如果你想最大值变小,那么 由 0->1 是由变化的,所以要留下 0 这个位置。
此时最大值 8 最小值 2,8-2>4 所以 l 应该向右移动。
l 向右移动 1,此时 l=1 所以 deq和inq中小于1的下标都应该被删除。
deq [1] inq [1] 最大值-最小值=1-1=0
此时子数组[1,1] 长度为1
r继续向右移动,r=2
deq[2] inq[1,2] // 对于值 deq[4] inq[2,4]
最大值-最小值 = 4-2 = 2<=4
此时子数组[1,2] 长度为2
r继续向右移动,r=3
deq[3] inq[1,2,3] // 对于值 deq[7] inq[2,4,7]
最大值-最小值 = 7-2 = 5>4,所以l需要向右移动
l=2, deq和inq中小于2的下标都应该被删除。
deq[3] inq[2,3] // 对于值 deq[7] inq[4,7]
最大值-最小值 = 7-4 = 3<=4
此时子数组[2,3] 长度为2
所以最长的子数组长度为2。
LeetCode 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit (绝对差不超过限制的最长连续子数组)的更多相关文章
- 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit
Given an array of integers nums and an integer limit, return the size of the longest continuous suba ...
- 【LeetCode】1438. 绝对差不超过限制的最长连续子数组 Longest Continuous Subarray With Absolute Diff Less Than or Equal t
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 滑动窗口 日期 题目地址:https://leetco ...
- 力扣1438. 绝对差不超过限制的最长连续子数组-C语言实现-中等难度
题目 传送门 文本 给你一个整数数组 nums ,和一个表示限制的整数 limit,请你返回最长连续子数组的长度,该子数组中的任意两个元素之间的绝对差必须小于或者等于 limit . 如果不存在满足条 ...
- [LeetCode] Shortest Unsorted Continuous Subarray 最短无序连续子数组
Given an integer array, you need to find one continuous subarray that if you only sort this subarray ...
- lintcode :continuous subarray sum 连续子数组之和
题目 连续子数组求和 给定一个整数数组,请找出一个连续子数组,使得该子数组的和最大.输出答案时,请分别返回第一个数字和最后一个数字的值.(如果两个相同的答案,请返回其中任意一个) 样例 给定 [-3, ...
- 【LeetCode】Maximum Product Subarray 求连续子数组使其乘积最大
Add Date 2014-09-23 Maximum Product Subarray Find the contiguous subarray within an array (containin ...
- Maximum Subarray 连续子数组最大和
Find the contiguous subarray within an array (containing at least one number) which has the largest ...
- leetcode面试题42. 连续子数组的最大和
总结一道leetcode上的高频题,反反复复遇到了好多次,特别适合作为一道动态规划入门题,本文将详细的从读题开始,介绍解题思路. 题目描述示例动态规划分析代码结果 题目 面试题42. 连续子数 ...
- [LeetCode] 674. Longest Continuous Increasing Subsequence_Easy Dynamic Programming
Given an unsorted array of integers, find the length of longest continuous increasing subsequence (s ...
- LeetCode 674. Longest Continuous Increasing Subsequence (最长连续递增序列)
Given an unsorted array of integers, find the length of longest continuous increasing subsequence. E ...
随机推荐
- 使用 Doxygen 来生成 Box2d 的 API 文档
对于 Doxygen 以前只听别人说过,而现在使用它也是一个偶然,缘分吧.前两天看 box2d 的官方 sdk 中,发现他有用户手册却没有说明,只是留下了一个 Doxygen 的文件.事情告一段落,然 ...
- OI生涯回忆&退役之后
一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的进程 --<庄子·秋水> 好吧,现在是2024年7月24日,我现在正坐在某编程机构的办公室电脑旁,写下这些文字,是啊,我已经退役将近两 ...
- Gradle配置文件解析和使用Meven本地仓库
Gradle配置文件 使用Gradle创建好项目之后,项目的根目录下会有一个build.gradle文件,该文件就是Gradle的核心配置文件 对应的信息: plugins { id 'java' } ...
- hive测试数据洗刷
hive测试--HIVE数据分析 测试使用虚拟机中的hive 环境:虚拟机+jdk+hadoop+hive+mysql 题目: 1.数据导入: 要求将样表文件中的(sales_sample_20 ...
- Jmeter函数助手8-counter
counter函数用于线程计数,类似计数器. TRUE每个用户有自己的计数器:FALSE使用全局计数器:即线程之间是否需要共享累加计数器,TRUE否,FALSE是 存储结果的变量名(可选) 1.线程之 ...
- 用了组合式 (Composition) API 后代码变得更乱了,怎么办?
前言 组合式 (Composition) API 的一大特点是"非常灵活",但也因为非常灵活,每个开发都有自己的想法.加上项目的持续迭代导致我们的代码变得愈发混乱,最终到达无法维护 ...
- 【RabbitMQ】07 SpringBoot整合RabbitMQ
生产者和消费者的依赖基本一致: 注意工程名称不是一样的 <?xml version="1.0" encoding="UTF-8"?> <pro ...
- 【Hibernate】03 配置文件 & API
映射器文件: - 字段的Column属性可以不写缺省,这将表示和实体类的属性标识一样 - type 属性用于声明表字段在Java中的类型,这个属性可不写缺省,自动匹配 Hibernate 4个核心AP ...
- 【Mybatis】Bonus02 补充
关于主键生成问题 Mybatis的主键生成是基于JDBC的使用主键[getGeneratedKeys()]方法 也就是说,必须要JDBC驱动的支持才行 @Test public void junitT ...
- 【Redis】05 持久化
持久化概述 Redis提供了不同的持久性选项: 1.RDB持久性按指定的时间间隔执行数据集的时间点快照. 2.AOF持久性会记录服务器接收的每个写入操作,这些操作将在服务器启动时再次播放,以重建原始数 ...