3134. 找出唯一性数组的中位数
给你一个整数数组 nums
。数组 nums
的 唯一性数组 是一个按元素从小到大排序的数组,包含了 nums
的所有
非空子数组中
不同元素的个数。
换句话说,这是由所有 0 <= i <= j < nums.length
的 distinct(nums[i..j])
组成的递增数组。
其中,distinct(nums[i..j])
表示从下标 i
到下标 j
的子数组中不同元素的数量。
返回 nums
唯一性数组 的 中位数 。
注意,数组的 中位数 定义为有序数组的中间元素。如果有两个中间元素,则取值较小的那个。
- 思路如下 :
一共有 m = n ∗ ( n + 1 ) 2 m=\frac{n*(n+1)}{2} m=2n∗(n+1) 种不同的取值,中位数 k = m + 1 2 k=\frac{m+1}{2} k=2m+1 。
中位数及其右边的数满足 : ≤ x \leq\; x ≤x 的对应值 ≥ k \geq \;k ≥k 个。
根据这条性质二分,至于求多少 ≤ x \leq\;x ≤x 的数据,滑动窗口即可。
class Solution {
public:// 计算有多少连续子数组满足其中不同元素的数量<=xlong long cal(vector<int> &nums, int x){int h = 0, t = -1, q[100010];int cnt[100010]; // 记录出现次数int s = 0; // 记录窗口中不同元素的数量long long res = 0;memset(cnt, 0, sizeof cnt);// x = 3, 窗口为 1 1 2 2 3 3, res += 窗口长度for(int i = 0; i < nums.size(); i ++){// 加入右端窗口q[++ t] = nums[i];if(cnt[nums[i]] ++ == 0) s ++;// 缩小窗口使得窗口中不同元素数量 <= x// 以 i 位端点满足条件的连续子数组 : 窗口长度while(s > x){if(-- cnt[q[h]] == 0){s --;}h ++;}res += t - h + 1;}return res;}int medianOfUniquenessArray(vector<int>& nums) {int n = nums.size();long long m = 1LL * n * (n + 1) / 2, k = (m + 1) / 2;int l = 1, r = n;while(l < r){int mid = l + r >> 1;// <= mid 元素的数量 >= k 个if(cal(nums, mid) >= k){r = mid;}else{l = mid + 1;}}return l;}
};// 首先一共有 m=n*(n+1)/2 个不同的取值, 中位数 k = (m+1)/2
// 中位数及其右边的数 x 满足 : <= x 的数据 >= k 个
// 现在就是统计数组中不同元素数量 <= x 的连续子数组的数量