Subarray With Elements Greater Than Varying Threshold
Subarray With Elements Greater Than Varying Threshold
You are given an integer array and an integer .
Find any subarray of of length such that every element in the subarray is greater than .
Return the size of any such subarray. If there is no such subarray, return .
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: nums = [1,3,4,3,1], threshold = 6 Output: 3 Explanation: The subarray [3,4,3] has a size of 3, and every element is greater than 6 / 3 = 2. Note that this is the only valid subarray.
Example 2:
Input: nums = [6,5,6,5,8], threshold = 7 Output: 1 Explanation: The subarray [8] has a size of 1, and 8 > 7 / 1 = 7. So 1 is returned. Note that the subarray [6,5] has a size of 2, and every element is greater than 7 / 2 = 3.5. Similarly, the subarrays [6,5,6], [6,5,6,5], [6,5,6,5,8] also satisfy the given conditions. Therefore, 2, 3, 4, or 5 may also be returned.
Constraints:
解题思路
因为的取值范围是,因此可以枚举。这里不可以用二分,因为不具有二段性。
从小到大枚举,那么随着的增大,会逐渐减小,因此满足要求的数会逐渐增多。在增大的过程中,如果发现某个数满足,那么就把它标记出来,为这个数的下标,这里的标记是指在数轴这个位置打个标记,表示这个位置上有数,一开始这个数轴是空的。因此每次增加的值,就逐渐会有满足要求的数加进数轴,数轴上加进来的数越来越多。每加入一个数,就判断与这个数相连的连续一段数的个数是否大于等于就好了。
因此我们需要找个数据结构来实现我们的操作。一开始区间(数轴)是空的,每次往里面添加一个元素,添加元素之后维护包括当前元素所在的连续被标记过的区间长度(如上图),看一下区间的长度是多少。我们可以用并查集来实现,不过这个并查集与一般的并查集不一样。
参考修改数组。一开始所有元素都没有被标记,因此并查集初始化时每个元素都指向自己。如果某个元素被标记了,那么这个元素就指向它的下一个位置的元素。因此每个集合中除了根节点(代表元素)外,其余的元素都是被标记过的(根元素指向自己因此没被标记)。还需要维护每个集合的大小,因此每个集合中被标记的元素个数就是当前集合的大小减去。
另外,因为我们是从小到大去枚举,因此为了快速知道哪些数满足(元素都是从大到小添加进来的),我们先对数组进行降序排序(数值和对应的下标进行排序,实际上我们只用到排序后这个数在原数组中的下标是多少)。
AC代码如下:
1 class Solution { 2 public: 3 vector<int> fa, idx, cnt; 4 5 int find(int x) { 6 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 7 } 8 9 int validSubarraySize(vector<int>& nums, int threshold) { 10 int n = nums.size(); 11 for (int i = 0; i <= n; i++) { 12 fa.push_back(i); 13 idx.push_back(i); // idx记录每个数在原始数组中的下标 14 cnt.push_back(1); 15 } 16 17 idx.pop_back(); // 把下标n弹出 18 sort(idx.begin(), idx.end(), [&](int a, int b) { // 根据原始数组中每个数的数值从大到小排序,最后idx就得到排序后每个数在原始数组中的下标位置 19 return nums[a] > nums[b]; 20 }); 21 22 for (int k = 1, i = 0; k <= n; k++) { 23 while (i < n && nums[idx[i]] > threshold / k) { 24 // idx[i]表示元素数组的的下标,idx[i]+1表示这个元素在原始数组的下标的下一个位置 25 cnt[find(idx[i] + 1)] += cnt[idx[i]]; 26 fa[idx[i]] = find(idx[i] + 1); // 当前元素指向下一个位置的元素 27 if (cnt[find(idx[i])] - 1 >= k) return k; 28 i++; 29 } 30 } 31 32 return -1; 33 } 34 };
其实不用这种并查集也是可以的。本质上是往区间加入一个元素,然后对这个元素左右两边的区间进行合并(如果可以合并的话),然后再求合并后的新的区间的长度,也就是动态维护连续的区间。用普通并查集也是可以做的。
AC代码如下:
1 class Solution { 2 public: 3 vector<int> fa, idx, cnt; 4 5 int find(int x) { 6 return fa[x] == x ? fa[x] : fa[x] = find(fa[x]); 7 } 8 9 int validSubarraySize(vector<int>& nums, int threshold) { 10 int n = nums.size(); 11 for (int i = 0; i < n; i++) { 12 fa.push_back(i); 13 idx.push_back(i); 14 cnt.push_back(0); // 这里初始化为0是为了方便表示当前元这个素还没有被标记 15 } 16 17 sort(idx.begin(), idx.end(), [&](int a, int b) { 18 return nums[a] > nums[b]; 19 }); 20 21 for (int k = 1, i = 0; k <= n; k++) { 22 while (i < n && nums[idx[i]] > threshold / k) { 23 cnt[idx[i]] = 1; // 当前元素被标记,赋值为1 24 if (idx[i] - 1 >= 0 && cnt[find(idx[i] - 1)]) { // 左边的元素所在区间已被标记,就把左边的集合与新元素合并 25 cnt[idx[i]] += cnt[find(idx[i] - 1)]; 26 fa[find(idx[i] - 1)] = idx[i]; 27 } 28 if (idx[i] + 1 < n && cnt[find(idx[i] + 1)]) { // 右边的元素所在区间已被标记,就把右边的集合与新元素合并 29 cnt[idx[i]] += cnt[find(idx[i] + 1)]; 30 fa[find(idx[i] + 1)] = idx[i]; 31 } 32 if (cnt[idx[i]] >= k) return k; 33 i++; 34 } 35 } 36 37 return -1; 38 } 39 };
参考资料
出题人表示,就是要深夜搞你们心态!力扣第82场双周赛:https://www.bilibili.com/video/BV1s94y197XG
C++匿名函数:https://www.cnblogs.com/Brickert/p/13164291.html
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/16637247.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效