LeetCode 3134.找出唯一性数组的中位数

给你一个整数数组 nums 。数组 nums 的 唯一性数组 是一个按元素从小到大排序的数组,包含了 nums 的所有非空 子数组 中不同元素的个数。

换句话说,这是由所有 0 <= i <= j < nums.length 的 distinct(nums[i…j]) 组成的递增数组。

其中,distinct(nums[i…j]) 表示从下标 i 到下标 j 的子数组中不同元素的数量。

返回 nums 唯一性数组 的 中位数 。

注意,数组的 中位数 定义为有序数组的中间元素。如果有两个中间元素,则取值较小的那个。

示例 1:

输入:nums = [1,2,3]

输出:1

解释:

nums 的唯一性数组为 [distinct(nums[0…0]), distinct(nums[1…1]), distinct(nums[2…2]), distinct(nums[0…1]), distinct(nums[1…2]), distinct(nums[0…2])],即 [1, 1, 1, 2, 2, 3] 。唯一性数组的中位数为 1 ,因此答案是 1 。

示例 2:

输入:nums = [3,4,3,4,5]

输出:2

解释:

nums 的唯一性数组为 [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3] 。唯一性数组的中位数为 2 ,因此答案是 2 。

示例 3:

输入:nums = [4,3,5,4]

输出:2

解释:

nums 的唯一性数组为 [1, 1, 1, 1, 2, 2, 2, 3, 3, 3] 。唯一性数组的中位数为 2 ,因此答案是 2 。

提示:

1 <= nums.length <= 10 5 ^5 5
1 <= nums[i] <= 10 5 ^5 5

一个数组有n*(n+1)/2个非空子数组,每个非空子数组对应一个distinct值,我们需要找出这些distinct值的中位数。如果有m个数字,中位数k为是m个数组中的第int((m+1)/2)小。这些distinct值的取值范围是1到nums.length,我们可以二分distinct值的取值范围,对于每次二分的mid值,找出有多少个子数组的distinct值小于k,从而找出第一个distinctRes值,使得distinct值小于该distinctRes值的子数组等于k个。在找distinct值小于该distinctRes值的子数组个数时,可用滑动窗口,窗口内的distinct值小于要查找的mid时,窗口内的任意子数组的distinct值也小于要查找的mid:

class Solution {
public:
    int medianOfUniquenessArray(vector<int>& nums) {
        int n = nums.size();
        long long subArrNum = (long long)n * (n + 1) / 2;
        long long k = (subArrNum + 1) / 2;

        auto check = [&](int upper) -> bool {
            int left = 0;
            unordered_map<int, int> cnt;
            long long arrNum = 0;
            for (int i = 0; i < n; ++i) {
                ++cnt[nums[i]];

                while (cnt.size() > upper) {
                    if (--cnt[nums[left]] == 0) {
                        cnt.erase(nums[left]);
                    }
                    ++left;
                }

                arrNum += i - left + 1;

				// distinct值小于upper的子数组数量大于等于k时,移动二分的右端点
                if (arrNum >= k) {
                    return true;
                }
            }
            return false;
        };

        int left = 1;
        int right = n;
        // 闭区间二分
        while (left <= right) {
            int mid = (left + right) / 2;
            check(mid) ? right = mid - 1 : left = mid + 1;
        }

        return left;
    }
};

如果nums的大小为n,则此算法时间复杂度为O(nlogn),空间复杂度为O(n),cnt中最多有n个元素。

你可能感兴趣的:(LeetCode,leetcode,算法,数据结构)