Subarray With Elements Greater Than Varying Threshold

Subarray With Elements Greater Than Varying Threshold

You are given an integer array nums and an integer threshold.

Find any subarray of nums of length k such that every element in the subarray is greater than threshold/k.

Return the size of any such subarray. If there is no such subarray, return 1.

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:

1nums.length105
1nums[i],threshold109

 

解题思路

  因为k的取值范围是1n,因此可以枚举k。这里不可以用二分,因为不具有二段性。

  从小到大枚举k,那么随着k的增大,Tk会逐渐减小,因此满足要求的数会逐渐增多。在增大k的过程中,如果发现某个数满足nums[i]>Tk,那么就把它标记出来,i为这个数的下标,这里的标记是指在数轴i这个位置打个标记,表示这个位置上有数,一开始这个数轴是空的。因此每次增加k的值,就逐渐会有满足要求的数加进数轴,数轴上加进来的数越来越多。每加入一个数,就判断与这个数相连的连续一段数的个数是否大于等于k就好了。

  因此我们需要找个数据结构来实现我们的操作。一开始区间(数轴)是空的,每次往里面添加一个元素,添加元素之后维护包括当前元素所在的连续被标记过的区间长度(如上图),看一下区间的长度是多少。我们可以用并查集来实现,不过这个并查集与一般的并查集不一样。

  参考修改数组。一开始所有元素都没有被标记,因此并查集初始化时每个元素都指向自己。如果某个元素被标记了,那么这个元素就指向它的下一个位置的元素。因此每个集合中除了根节点(代表元素)外,其余的元素都是被标记过的(根元素指向自己因此没被标记)。还需要维护每个集合的大小,因此每个集合中被标记的元素个数就是当前集合的大小减去1

  另外,因为我们是从小到大去枚举k,因此为了快速知道哪些数满足nums[i]>Tk(元素都是从大到小添加进来的),我们先对数组进行降序排序(数值和对应的下标进行排序,实际上我们只用到排序后这个数在原数组中的下标是多少)。

  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

posted @   onlyblues  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示