给你一个整数数组 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; } };