题解 | 找出唯一性数组的中位数

alt

题干解析:

题目大意如下:

  • 首先对于一个长为n的数组,其至少有n*(n-1)/2个子数组,将这些子数组中所包含的元素个数作为元素记录在另一个数组中,同时确保数组升序,我们得到的数组即为题设的唯一性数组
  • 然后题目要求我们寻找这个升序数组的中位数,同时如果中位数有两个,则取最小的那个.

算法逻辑:

最先想到的几乎都是直接暴力枚举出唯一性数组,然后取出其中的中位数,并返回,但这里数据量最高为 n=100000,因此,直接枚举的话需要循环10^10次,显然会超时,再者,在分析题目时我刻意没有说我们需要得到完整的唯一性数组,确实,针对这道题,我们没必要得到完整的唯一性数组.

首先我们确定数组的中位数所在位置k,假设一个数组有n个值,那么中位应当为: alt

针对唯一性数组,我们要么找到此数组的低于一个数upper的值有少于k个,那么我们便能确定中位数ans > upper,反之ans <= upper.然后唯一性数组值小于upper的个数意味着什么呢,upper意味着某个子数组元素个数为upper,因此这个值意味着数组的所有子数组中元素个数不大于upper的个数.

由此我们不妨使用双指针或滑动窗口对数组进行划分子数组,同时计数.不仅如此,不难发现,一个子数组的子数组其元素个数一点小于等于该子数组,针对这个特性我们便能够省去反复前移右指针的位置(即右指针只需要一直右移即可,达到最大的元素个数(upper)时在计数即可).

现在我们能够依据upper来压缩ans的取值范围了,之后的操作便是通过二分法反复压缩ans的取值范围直至将其逼至某个值,那个值便是我们所需的答案.

实现代码:

int medianOfUniquenessArray(vector<int>& nums) {
    const int n = static_cast<int>(nums.size());
    const int64_t k = (static_cast<int64_t>(n) * (n + 1) / 2 + 1) / 2; // k值可能很大,因此使用long long(int64_t)

    auto check = [&](const int upper) -> bool {
        int64_t cnt = 0; // 符合要求的子数组个数
        int left =0; // 左指针
        unordered_map<int, int> freq; // 计频器,可以用数组代替
        for (int right = 0; right < n; ++right) {
            freq[nums[right]]++;
            while (freq.size() > upper) { // 窗口元素个数多于了upper,不符合要求了,因此需要去除至少一种元素
                int out = nums[left];
                ++left;
                --freq[out];
                if (freq[out] == 0) freq.erase(out);
            }
            cnt += right - left + 1; // 累加符合要求的子数列个数,这里子数列均保证以left为起点,因此是线性的,不与要相乘
            if (cnt >= k) return true; // 有多于k个的窗口数(子数列数)
        }
        return false; // 符合要求窗口总数小于k个
    };

  	// 二分模板,但注意边界与判断函数返回的布尔值的含义
    int left = 0, right = n;
    while (left + 1 < right) {
        if (const int mid = (right + left) / 2; check(mid)) right = mid;
        else left = mid;
    }

    return right;
}

复杂度分析:

  • 时间复杂度: 二分模板基础复杂度为O(log n), 针对判断函数,每个窗口元素最多进出一次窗口,总计为O(n),二者结合,总时间复杂度为O(nlog n).
  • 空间复杂度: 哈希表计数开销为O(n),其余额外空间开销为O(1),总计O(n).
全部评论

相关推荐

11-24 23:39
门头沟学院 Java
贴主开始投日常差不多一个小阶段一个大阶段第一个小阶段六月开投到七月6.24&nbsp;腾讯天美一面挂6.25&nbsp;蔚来武汉一面挂7.8&nbsp;字节番茄小说一面挂第一个小阶段只有三个面试,腾讯处男面,当时算法都没怎么刷,八股也没背熟就上了(笑),到现在腾讯还没再约,感觉是面评脏完了),字节面的学到很多,发现简历上有些问题,对项目理解加深了点。然后七月份离校完回家,在家里gap(),错过了八月份面试黄金时期,悔不当初,希望大家引以为戒。第二个阶段九月到目前9.8,9.12,9.16,9.17,9.24一共五个中小厂面,只oc一个(笑)中小厂感觉对项目问的很多,大家面大厂之前可以多投点中小厂对项目熟悉熟悉。9.23&nbsp;快手音视频一面挂10.9&nbsp;字节抖音直播一面挂10.10百度百家号一面10.15&nbsp;百度百家二面挂10.24&nbsp;懂车帝一面挂10.24&nbsp;百度网盘一面10.29&nbsp;百度网盘二面挂10.30&nbsp;虾皮一面挂11.7&nbsp;字节业务中台一面挂11.12&nbsp;快手&nbsp;ai&nbsp;应用一面11.14&nbsp;字节tt生服一面11.19&nbsp;快手&nbsp;ai&nbsp;应用二面挂说实话这种战绩看着太想笑了,基本全是一面挂。百度网盘二面完拖了好久被挂;快手二面是最难受的,算上自我介绍反问只有15min,无手撕,反问还问了面试官为什么这么短,他说就了解下基本情况。当时我已经笑了,面完还觉得稳了,但是又不敢提前开香槟。结果周三面完一直没出结果,后边几天一直煎熬,甚至11.24周一问的时候这个岗位还换了个hr,最终还是挂。看到hr发过来没通过消息的时候人真的红了,要是没答好被挂心甘情愿,但是15分钟里他问的问题,项目八股都回答上来了,还是挂,真的怀疑人生了。贴主自制力其实挺差,没有面试的时候基本没有学习动力,只有有面试的时候才会push自己去刷算法背八股,闲着没事就打apex(笑)。所以这个结果某种程度上也是理所当然,或许就是给过去偷的懒还债吧。贴主还是条0实习的区,这个时间段找实习感觉基本都是至少一段实习,牛油们一定要早实习,哪怕是小厂也比没实习强。这个帖子算是些贴主的碎碎念,也是对目前的一些总结吧,从九月份到十一月快结束了,三个月一直刷boss投简历,心好累,感觉大厂或许是我遥不可及的梦吧。欢迎牛油大佬们来评论~要是有对贴主的建议那就更好了(),感谢大家看到这里。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务